多线程的创建方式
一:进程与线程
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
2:实现多线程有三种方法:一种是继续Thread类,另外一种是实现Runable接口.第三种是实现Callable接口通过FutureTask包装器来创建Thread线程
//继承Thread类 public class MyThread extends Thread{ public void run(){ //重写run方法 } } new MyThread().start();//创建并启动线程 //实现Runnable接口 public class MyThread2 implements Runnable { public void run(){ //重写run方法 } } //创建并启动线程 MyThread2 myThread=new MyThread2(); Thread thread=new Thread(myThread); thread().start(); //实现Callable接口,使用FutureTask包装 FutureTask<String> ft = new FutureTask<String>(new Callable<String>() { @Override public String call() throws Exception { System.out.println("new Thread 3");//输出:new Thread 3 return "aaaa"; } }); Thread t3 = new Thread(ft); t3.start(); String result = ft.get();//法会阻塞,直到子线程执行结束才返回 System.out.println(result);//输出:aaaa }
直接调用线程的run方法时,并不是多线程,只是一个普通顺序方法。必须调用start方法才是多线程的调用方式
3.区别
实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,前者线程执行体run()方法无返回值,因此可以把这两种方式归为一种这种方式与继承Thread类的方法之间的差别如下:
<1、线程只是实现Runnable或实现Callable接口,还可以继承其他类。
<2、这种方式下,多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。
<3、但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。
<4、继承Thread类的线程类不能再继承其他父类(Java单继承决定)。
4.线程状态转换
<1、新建状态(New):新创建了一个线程对象。
<2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
<3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
<4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
<5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
注:一般推荐采用实现接口的方式来创建多线程
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实际就是在操作系统中启动了一个进程。