前置Thread线程基础-并行和并发
并行就是同时执行,并发就是在交替执行
在操作系统中,安装了很多程序,并发指的是在一段时间内宏观上多个程序同时执行,这个在单个CPU系统中,每一个时刻只有一个程序执行,即微观上这些程序是分时交替的执行,只不过给人感觉是在同时运行,因为分时交替运行时间非常短暂
现在而言都是多核CPU,则这些并发执行程序可以分配到不同的处理器上(CPU),实现多个任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序就可以同时执行了,你的电脑CPU核心越多你电脑的性能就相对更加强悍
PS:单核处理的计算机肯定是不能并行处理多个任务,只能是多个任务在单个CPU上并发需要运行,同理线程也是一样的从宏观的角度而言线程并行运行从微观角度看就是串行运行,即线程是一个一个去执行的,当系统只有一个CPU是,线程会以某种顺序执行多个线程,这个情况称之线程调度【线程提供CPU时间争抢】
17.1 进程和线程
17.1.1 进程
进程:程序是静止的,只有真正运行时的程序,才被称为进程。
进程是程序的一次执行过程,是系统运行程序的基本单元,系统运行一个程序即在运行一个进程【从创建、运行、消亡的一个过程】,每一个进程都有自己一个独立的空间【内存空间】,一个应用程序(进程)可以同时运行多个线程
特点:
- 单核CPU在任何时间点上。
- 只能运行一个进程。
- 宏观并行、微观串行。
- 进程是一个独立程序单元并且拥有自己独立空间
- 一个进程中可以同时运行多个线程
- 进程是可以完成多个任务交替执行的,这样做开发成本高,进行是独立的进程与进程之间是无法进行“通信”的
17.1.2 线程
线程:又称轻量级进程(Light Weight Process)。
- 线程是在进程的内部执行,并且可以存在多个,彼此之间共享进程的内存区域
- 程序中的一个顺序控制流程,同时也是CPU的基本调度单位。
- 进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。
比如:
- 迅雷是一个进程,当中的多个下载任务即是多个线程。
- Java虚拟机是一个进程,默认包含主线程(main),通过代码创建多个独立线程,与main并发执行。
- 可以利用这个轻量级的开发完成一个进程内部程序交替执行效果(线
程并发执行)
17.1.3 进程和线程区别
- 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
- 一个程序运行后至少有一个进程。
- 一个进程可以包含多个线程,但是至少需要有一个线程。
- 进程间不能共享数据段地址,但同进程的线程之间可以。
进程: 有独立的内存空间,进程中数据存放的空间是独立的并且至少有一个线程
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源要比进程小,并且可以多个线程存在同一个进程中
PS:需要知道了解的知识点:
- 因为一个程序中有多个线程并发运行,那么从微观的角度而言是有先后顺序的,那么线程执行的顺序是取决于CPU的调度【线程争抢CPU时间片】,程序猿只能进行干涉,在不加干涉前提下线程执行就会出现很多随机性
- Java程序进程中最少包含两个线程,一个是主线程就是main()方法【它在执行方法是或者执行程序时它的优先级永远最高】,另外一个垃圾回收机制线程(GC)【守护线程】,每当Java执行一个类的时候,实际上都会启动一个JVM,
每一个JVM实际上就是在操作系统中启动了一个线程,Java本身就被垃圾回收机制所守护,所以Java运行时至少启动了两个线程
- 由于创建一个线程开销远比创建一个进程开销要小很多,那么我们在开发多任务运行时,通常会优先考虑创建多线程,而不是多进程
- 所有的线程轮流使用CPU,每一个线程都会得到CPU分配的时间片,这个分配“尽量”平均分配CPU资源,但是这个效果不一定能达到,所以同优先级的线程对CPU时间的争抢是存在随机性的,可以对线程进行优先级设置来概念获取CPU时间片的几率,优先级越高几率越大,优先级越低几率越小
17.1.4 线程组成
任何一个线程都具有基本的组成部分:
- CPU时间片:操作系统(OS)会为每个线程分配执行时间。
- 运行数据:
堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象。
栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。- 线程的逻辑代码。
17.2 创建线程
Java中创建线程主要有两种方式:
- 继承Thread类。此时子类就是线程类
- 实现Runnable接口。此时实现类不是线程类,使用Thread类才可
成为线程
无论是上述两种方式中那种方式都需必须重写run方法实现线程逻辑
- 实现Callable接口, 此实现方方式并不是线程类,主要针对的是线程池提供
- 线程池可以帮组我们创建线程并进行管理操作
如果实现线程需要继承Thread或实现Runnable接口,但是无论是那种都需要实现一个必要核心方法
这个run方法是提供线程实现的核心逻辑,需要线程什么样需求代码,就将将代码写入到这个线程中
17.2.1 继承Thread类
步骤:
- 编写类、继承Thread。
- 重写run方法。
- 创建线程对象。
- 调用start方法启动线程。
案例演示:
MyThread类:
public class MyThread extends Thread { public MyThread() { // TODO Auto-generated constructor stub } public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++) { System.out.println("子线程:"+i); } } }
TestMyThread类:
public class TestThread { public static void main(String[] args) { //1创建线程对象 MyThread myThread=new MyThread(); myThread.start();//myThread.run() //创建第二个线程对象 MyThread myThread2=new MyThread(); myThread2.start(); //主线程执行 for(int i=0;i<50;i++) { System.out.println("主线程======================"+i); } } }
获取线程名称:
- getName()。
- Thread.currentThread().getName()。
public void run() { for(int i=0;i<100;i++) { //this.getId获取线程Id //this.getName获取线程名称 //第一种方式 this.getId和this.getName(); //System.out.println("线程id:"+this.getId()+" 线程名称:"+this.getName()+" 子线程............."+i); //第二种方式 Thread.currentThread() 获取当前线程 System.out.println("线程id:"+Thread.currentThread().getId()+" 线程名称:"+Thread.currentThread().getName()+" 子线程。。。。。。。"+i); } }
public static void main(String[] args) { //1创建线程对象 MyThread myThread=new MyThread("我的子线程1"); //2启动线程,不能使用run方法 //修改线程名称 //myThread.setName("我的子线程1"); myThread.start();//myThread.run() //创建第二个线程对象 MyThread myThread2=new MyThread("我的子线程2"); //myThread2.setName("我的子线程2"); myThread2.start(); //主线程执行 for(int i=0;i<50;i++) { System.out.println("主线程======================"+i); } }
17.2.2 应用案例
实现四个窗口各卖100张票。
TicketWin类
public class TicketWin extends Thread{ public TicketWin() { // TODO Auto-generated constructor stub } public TicketWin(String name) { super(name); } private int ticket=100;//票 @Override public void run() { //卖票功能 while(true) { if(ticket<=0) { break; } System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票"); ticket--; } } }
TestWin类:
public class TestWin { public static void main(String[] args) { //创建四个窗口 TicketWin w1=new TicketWin("窗口1"); TicketWin w2=new TicketWin("窗口2"); TicketWin w3=new TicketWin("窗口3"); TicketWin w4=new TicketWin("窗口4"); //启动线程 w1.start(); w2.start(); w3.start(); w4.start(); } }
17.2.3 实现Runnable接口
步骤:
- 编写类实现Runnable接口、并实现run方法。
- 创建Runnable实现类对象。
- 创建线程对象,传递实现类对象。
- 启动线程。
案例演示:
MyRunnable类:
public class MyRunnable implements Runnable{ @Override public void run() { for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName()+" ........."+i); } } }
TestMyRunnable类:
public class TestRunnable { public static void main(String[] args) { //1创建MyRunnable对象,表示线程要执行的功能 MyRunnable runnable=new MyRunnable(); //2创建线程对象 Thread thread=new Thread(runnable, "我的线程1"); //3启动 thread.start(); for(int i=0;i<50;i++) { System.out.println("main............"+i); } } }
17.2.4 课堂案例
实现四个窗口共卖100张票。
Ticket类:
public class Ticket implements Runnable { private int ticket=100;//100张票 @Override public void run() { while(true) { if(ticket<=0) { break; } System.out.println(Thread.currentThread().getName()+" 卖了第"+ticket+"张票"); ticket--; } } }
TestTicket类:
public class TestTicket { public static void main(String[] args) { //1创建票对象 Ticket ticket=new Ticket(); //2创建线程对象 Thread w1=new Thread(ticket, "窗口1"); Thread w2=new Thread(ticket, "窗口2"); Thread w3=new Thread(ticket, "窗口3"); Thread w4=new Thread(ticket, "窗口4"); //3启动线程 w1.start(); w2.start(); w3.start(); w4.start(); } }
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(中):https://developer.aliyun.com/article/1580255