分享

第十一讲:多线程与多线程

 夜猫速读 2022-05-05 发布于湖北

1、什么是进程

狭义:进程是正在运行的程序实例

程序是指令和数据的有序集合、其本身没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的一次执行过程,它是一个动态的概念。

进程是有一个具有一定独立功能的程序、一个实体、每一个进程都有它自己的地址空间。

广义:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度的基本单位

进程的概念要注意两点:

进程是一个实体。每一个进程都有它自己的独立地址空间

进程是一个“执行中的程序”。只有当处理器执行这个程序时,它成为活动的实体

2、进程的状态

进程执行时间的间断性,决定了进程可能具有多种状态、事实上的进程具有以下基本状态。

1)就绪状态(Ready)

2)运行状态(Running)

3)  阻塞状态(BIocked)

线程是进程中的一个实体,是CPU调度的基本单位,只拥有在运行中必不可少的资源,但它可与同属一个进程的其他线程共享所拥有的资源。

3.线程和进程的关系

线程是进程的一个实体,一个进程可以拥有多个线程。

4.进程和线程的区别

(1) 地址空间:同一进程的所有线程共享本进程的地址空间,不同进程之间的地址空间是相互独立的。

(2) 资源拥有:同一进程内的线程共享本进程的资源如:内存、I/O,CPU等,不同的进程资源相互独立。

(3) 健壮性:一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃后,会终结整个进程。所以多进程要比多线程健壮。

(4) 线程切换比进程切换消耗的资源更少、速度更快。

(5) 线程是CPU调度的基本单位,进程是系统资源分配的基本单位。

1、什么是线程:

在一个进程中,每个独立的功能都需要独立的去运行,这时又需要把当前这个进程划分成多个运行区域,每个独立的小区域(小单元)称为一个线程。

例如:360杀毒软件,同时既可以安全体检电脑又可以清理电脑中的垃圾。那么这里的安全体检是360杀毒软件中的一个线程,清理电脑中的垃圾也是一个线程。

2、什么是多线程:

一个进程如果只有一条执行任务,则称为单线程程序。

一个进程如果有多条执行任务,也就是说在一个进程中,同时开启多个线程,让多个线程同时去完成某些任务(功能)。则称为多线程程序。

并发:指应用能够交替执行不同的任务,其实并发有点类似于多线程的原理,多线程并非是同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不断去切换这两个任务,以达到"同时执行效果",其实并不是的,只是计算机的速度太快,我们无法察觉到而已.

就类似于你,吃一口饭喝一口水,以正常速度来看,完全能够看的出来,当你把这个过程以n倍速度执行时..可以想象一下, 吃->喝....如此反复.

什么是并行?

并行:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话,这两件事情可以同时执行

两者区别:一个是交替执行,一个是同时执行.

二、线程的基本使用

package om.vince1;//线程实现的两种方式public class ThreadDemo1 {    public static void main(String[] args) {         MyThread mt=new MyThread();          //推荐        MyRunnable mr=new MyRunnable();        Thread t2=new Thread(mr);        mt.start();//启动线程        t2.start();    }}/** * 实现线程的第一种方式、继承thread类 * 逻辑处理 */class MyThread extends Thread{    public  void run(){     super.run();     for(int i=0;i<50;i++){         System.out.println( i );         System.out.println( Thread.currentThread().getName()+"-"+i );     }    }}/**实现线程的第二种方式:实现Runnable接口 * 逻辑处理 */class MyRunnable implements Runnable{    @Override    public void run() {        for(int i=0;i<500;i++){            System.out.println( Thread.currentThread().getName()+"-"+i );            System.out.println( Thread.currentThread().getName()+"+"+i );        }    }}

程序中的多个线程是并发执行的,某个线程若想被执行必须要得到CPU的使用权,Java虚拟机会按照特定的机制为程序中的每个线程分配CPU的使用权,这种机制被称作线程的调度。

两种线程调度模式:

分时调度模式:让所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用的CPU的时间片。

抢占式调度模式:让可运行池中优先级高的线程优先占用CPU,而对于优先级相同的线程,随机选择一个线程使其占用CPU,当它失去了CPU的使用权后,再随机选择其他线程使其占用CPU。

Java虚拟机默认采用抢占式调度模型。

一、线程的优先级

线程的优先级用1~10之间的整数来表示,数字越大优先级越高。Thread类提供三个静态常量表示线程的优先级:

MAX_PRIORITY :相当于10

MIN_PRIORITY:相当于1

NORM_PRIORITY:相当于5

通常用setPriority(int 优先级)对线程进行设置,但优先级调整的是几率,而不是将优先级低的进程挂起。setPriority 告诉JVM这个线程的优先级,但JVM是否按你请求 (请求,不是要求)办不确定,就是结果不确定难过(最主要原因是这些优先级需要操作系统的支持,不同的操作系统对优先级的支持是不一样的)

二、线程休眠

运用sleep()函数,使正在执行的线程暂停一段时间,将CPU让给别的线程,即让线程进入休眠等待状态。

注意:①sleep()释放资源不释放锁

        ②sleep(long millis)方法生命抛出InterruptedException异常,因此在调用该方法时应该捕获异常,或声明抛出该异常。

throws lnterruptedException使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)释放CPU的时间片,具体取决于系统定时器和调度程序的精度和准确度,线程不会丢失任何显示器的所有权。

lnterruptedException如果任何线程中断当前线程,当抛出异常时,当前线程的中断状态将被清除。

static Thread currenThread()返回对当前正在执行的线程对象的引用。


//定义SleepThread类实现Runnable接口class SleepThread implements Runnable { public void run() { for (int i = 1; i <= 10; i++) {   System.out.println("线程一正在输出:" + i); } }}public class Example2 { public static void main(String[] args) throws Exception { // 创建一个线程 new Thread(new SleepThread()).start(); for (int i = 1; i <= 10; i++) { if (i == 5) { System.out.println("主线程暂时休眠500毫秒,接下来是线程一输出"); Thread.sleep(2000); // 当前线程休眠2秒 } System.out.println("主线程正在输出:" + i); } }}

结果:

线程休眠只是让该线程停一段时间,一段时间之后就可以接着进行,不需要别的线程进行完,例子中因为线程一总执行时间较短,在主线程休眠的时间内就运行完了。

三、线程让步

通过yield()方法,将当前进程停下,换成就绪状态,让系统的调度器重新调度一次。

与sleep()方法相似,但yield()方法不会阻塞该线程,之后该线程与其他线程是相对公平的。调度谁看系统,有可能还是调度它自己。

class YieldThread extends Thread {    // 定义一个有参的构造方法 public YieldThread(String name) {   super(name); // 调用父类的构造方法 } public void run() {  for (int i = 0; i < 10; i++) {   System.out.println(Thread.currentThread().getName() + "---" + i);   if (i == 3) {    System.out.print("线程让步:\n");    Thread.yield(); // 线程运行到此,作出让步   }  } }}public class Example2 { public static void main(String[] args) {        // 创建两个线程  Thread t1 = new YieldThread("线程A");  Thread t2 = new YieldThread("线程B");        // 开启两个线程  t1.start();  t2.start(); }}

结果:

通过结果可以看到线程让步之后,有可能系统调度的是别的线程,有可能还是自己。

四、线程插队

就像它的名字,插队,通过join()方法,阻塞当前线程,先完成被join()方法加入的插入的这个线程,之后再完成其他线程。


class EmergencyThread implements Runnable { public void run() { for (int i = 1; i < 7; i++) { System.out.println(Thread.currentThread().getName()+"输出:"+i); } }} public class Example2{ public static void main(String[] args) throws Exception { // 创建线程 Thread t = new Thread(new EmergencyThread(),"线程一"); t.start(); // 开启线程 for (int i = 1; i < 7; i++) { System.out.println(Thread.currentThread().getName()+"输出:"+i); if (i == 2) { //System.out.println("线程1插队"); t.join(); // 调用join()方法 } } }}

结果:

从结果可以看到,当main线程输出2以后,线程一就开始执行,直到线程一执行完毕,main线程才继续执行。

join与中断线程

public final void join() throws InterruptedException

等待这个线程死亡。

调用此方法的行为方式与调用完全相同

join (0)

异常 InterruptedException - 如果任何线程中断当前线程。当抛出此异常时,当前线程的中断状态将被清除。

public void interrupt()

中断这个线程。

除非当前线程中断自身,这是始终允许的

public static boolean interrupted()

测试当前线程是否中断。该方法可以清除线程的中断状态 。换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false(除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经检查过)。

忽略线程中断,因为线程在中断时不存在将被该方法返回false所反映。

自定义标记中断线程

 package com.vince;/** *  * join方法: * 加入线程,让调用的线程先执行指定时间或执行完毕 * 中断线程: * (1)使用interrupt方法来中断线程,设置一个中断状态(标记) * (2)自定义标记的方式(推荐使用) */public class ThreadDemo2 {
public static void main(String[] args){
MyRunable2 mr2 = new MyRunable2(); Thread t = new Thread(mr2);// t.start();
MyRunable3 mr3 = new MyRunable3(); Thread t2 = new Thread(mr3); t2.start();
for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName()+"--"+i); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } if(i==20){// try {// t.join();//让t线程执行完毕// } catch (InterruptedException e) {// e.printStackTrace();// }// t.interrupt();//中断线程,只是作了一个中断标记 mr3.flag = false; } } }}
class MyRunable2 implements Runnable{ @Override public void run() { for (int i = 0; i < 50; i++) { if(Thread.interrupted()){//测试中断状态,此方法会把中断状态清除 //.... break; } System.out.println(Thread.currentThread().getName()+"--"+i); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } } }}
class MyRunable3 implements Runnable{ public boolean flag = true; public MyRunable3(){ flag = true; } @Override public void run() { int i=0; while(flag){ System.out.println(Thread.currentThread().getName()+"==="+(i++)); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } }}

守护线程与yield

public final void setDaemon(boolean on)

将此线程标记为daemon线程或用户线程。当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。

public final boolean isDaemon()

测试这个线程是否是守护线程。

public static void yield()

暂停当前正在执行的线程对象,并执行其他线程。(了解)

package com.vince;import javax.sound.midi.Soundbank;public class ThreadDemo3 {    public static void main(String[] args){        MyRunnable4 mr4 = new MyRunnable4();        Thread t = new Thread(mr4);        t.setName("Thread-t");        //优先级高可以提高该线程抢点CPU时间片的概率大        t.setPriority(Thread.MAX_PRIORITY);        //线程可以分成守护线程和 用户线程,当进程中没有用户线程时,JVM会退出        t.setDaemon(true);//把线程设置为守护线程        System.out.println(t.isAlive());        t.start();        System.out.println(t.isAlive());
for (int i = 0; i < 50; i++) { System.out.println("main--"+i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } if (i==5){ Thread.yield();//让出本次CPU执行时间片 }        } }}class MyRunnable4 implements Runnable{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("--"+i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }}

第一步 简单测试下 将t线程设置为守护线程

让main执行的快一点 当main执行完后 t还会不会继续执行

注意在启动线程之前调用

结果 不会执行 因为用户线程执行完了 只剩守护线程时 会退出JVM线程可以分成守护线程和 用户线程,当进程中没有用户线程时,JVM会退出

用处 当你使用一个程序要退出时 主线程执行完毕 有一些工作 比如统计后台数据(当然是不需要保存的无关紧要的数据)什么的这些线程就可以设置为守护线程

主线程执行完毕 这些工作就没必要继续执行了

yield

public static void yield()

暂停当前正在执行的线程对象,并执行其他线程。(了解)

Thread.yield();//让出本次CPU执行时间片 只让一次

static int MAX_PRIORITY 线程可以具有的最高优先级。

static int MIN_PRIORITY 线程可以具有的最低优先级。

static int NORM_PRIORITY 分配给线程的默认优先级。

线程同步

1、多线程共享数据

在多线程操作中、多个线程有可能同时处理同一个资源,这就是多线程中的共享数据。

2、线程同步

解决数据共享问题、必须使用同步,所谓同步就是指多个线程同一个时间段内只能有一个线程执行指令代码,其它线程要等待此线程完成之后才可以继续执行。

(1)线程进行同步

synchronized(要同步的对象){

要同步的操作

}

(2)同步方法

public synchronized void method(){

要同步的操作;

}

(3)Lock(ReentrantLock)

package om.vince1;
import java.util.concurrent.locks.ReentrantLock;
/** * 1、多线程共享数据时,会发生线程不安全的情况 * 2、多线程共享数据必须使用同步、 * 3、同步准则 * 当编写synchronized时,有几个准则需要遵循,这些准则在避免死锁和性能危险 * 的风险方面大有帮助 * (1)使用代码块保持简短,把不随线程变化的预处理和后处理移出synchronized块 * (2)不要阻塞,如:lnputStream.read(); * (3)在持有锁的时候、不要对其它对象调用方法 */public class ThreadDemo4 { public static void main(String[] args) { MyRunnable5 mr5 = new MyRunnable5( ); Thread t1 = new Thread(mr5); Thread t2 = new Thread(mr5); t1.start( ); t2.start( ); }}
class MyRunnable5 implements Runnable { private int ticket = 10;//售票 private Object obj = new Object( );//同步锁
@Override public void run() { for (int i = 0; i < 300; i++) { method( ); /*if (ticket > 0) { synchronized (this){
} ticket--; System.out.println( "您购买的票已剩余" ); try { Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace( ); } System.out.println( "您购买的票 "+ticket--+"张" ); }*/ } }

//同步方法:同步的对象时当前对象(this) private synchronized void method() { if (ticket > 0) { synchronized (this) {
} ticket--; System.out.println("您购买的票已剩余"); try { Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace( ); } System.out.println("您购买的票 " + ticket-- + "张"); } }//互斥锁 ReentrantLock lock=new ReentrantLock( ); //Lock实现同步 private void method2() { lock.lock();//锁 try { if (ticket > 0) { synchronized (this) {
} ticket--; System.out.println("您购买的票已剩余"); try { Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace( ); } System.out.println("您购买的票 " + ticket-- + "张"); }
}finally { } lock.unlock();//释放锁 }}

死锁

过多的同步有可能出现死锁,死锁的操作一般在程序运行的情况出现

多线程要进行资源的共享,就需要同步、但同步过多、就可能造成死锁。

1 如何才能产生死锁

产生死锁的四个必要条件:

互斥条件:所谓互斥就是进程在某一时间内独占资源。

请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。

循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

2 Java死锁的简单例子

两个线程互相占有对方需要的资源而不释放,便形成了死锁。


/** * 程序类 */public class Program implements Runnable { //两个静态的资源 public static Object obj1 = new Object(); public static Object obj2 = new Object(); //标志位 public int flag = 1; //1 or 2 @Override public void run() { if(flag == 1) { synchronized (obj1) { System.out.println("flag: " + flag + ", 锁住了资源obj1"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("flag: " + flag + ", 等待获取资源obj2"); synchronized (obj2) { System.out.println("flag: " + flag + ", 获得资源obj2"); } } } else if(flag == 2) { synchronized (obj2) { System.out.println("flag: " + flag + ", 锁住了资源obj2"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("flag: " + flag + ", 等待获取资源obj1"); synchronized (obj1) { System.out.println("flag: " + flag + ", 获得资源obj1"); } } } } }



/** * 死锁测试 */public class DeadLock {  public static void main(String[] args) {    Program p1 = new Program();  Program p2 = new Program();  p1.flag = 1;  p2.flag = 2;    new Thread(p1).start();  new Thread(p2).start(); }}

生产者和消费者问题是一个经典的线程(进程)同步问题。他们之间有一个共用的缓存区,在缓存区为空时生产者生产物品投入缓存池,缓存池非空的时候,消费者就可以从缓存池中取走物品。

package com._520it.day02._producer_consumer; // 共享的资源public class ShapedResource { private String name ;  private String gender ;    synchronized public void push(String name , String gender ) {  this.name = name ;   try {   Thread.sleep(10);  } catch (InterruptedException e) {   // TODO Auto-generated catch block   e.printStackTrace();  }  this.gender = gender ;  } synchronized public void pop() {  try {   Thread.sleep(10);  } catch (InterruptedException e) {   // TODO Auto-generated catch block   e.printStackTrace();  }  System.out.println(this.name +"-"+this.gender); } }
package com._520it.day02._producer_consumer;// 生产者public class Producer implements Runnable{ private ShapedResource resource =null ; public Producer(ShapedResource resource) {  this.resource = resource ;  } public void run() {    for(int i = 0 ; i<50 ; i++ ) {   if(i %2 == 0 ) {       resource.push("阿华""男");   }   else {    resource.push("阿花","女") ;    }  } } }

package com._520it.day02._producer_consumer; public class Consumer implements Runnable{ private ShapedResource resource = null ; public Consumer(ShapedResource resource) { this.resource = resource ; } public void run() { for(int i = 0 ; i<50 ;i++ ) { resource.pop(); } }}

问题一 : 出现性别紊乱 

       解决方案: 只有保证生产姓名和性别过程保持同步,中间不能让消费者取走树

       使用同步代码块/同步方法/lock机制。

问题二 : 应该生产一个数据,消费一个数据

       解决方案: 得使用等待和唤醒机制。

同步锁池 : 

     同步锁必须选择多个线程共同的资源对象. 

     当生产者在生产数据的时候(先拥有同步锁),其他线程就在锁池中等待获取锁, 当线程执行问同步代码时,就会释放同步锁,其他线程就可以开始抢锁的使用权

线程通信-wait 和notify方法 :

java.lang.Object类提供类两类用于操作线程通信的方法。wait(): 执行该方法的线程对象释放同步锁,jvm把该线程存放到等待池中,等待其他线程唤醒该线程。

notify() : 执行该方法的线程对象唤醒在等待池中的任意一个线程,把线程转到锁池中等待(等待被调度)。 

notifyAll(): 执行该方法的线程对象唤醒在等待池中的所有线程,把线程转到锁池中等待。

注意 :  上述方法只能被同步监听锁对象来调用,否则报错 

假设 A 线程和 B线程共同操作一个x对象(同步锁) ,A,B线程可以通过x对象的wait和 notify方法来进行通信。

流程 : 

   当A线程执行x对象的同步方法时,A线程持有x对象的锁,B线程没有执行机会,B线程在x对象的锁池中等待。

   A线程在同步方法中执行x.wait()方法时,A线程释放x对象的锁,进入x对象的等待池中。

   在x 对象的锁池中等待的B线程获取到 x 对象的锁,执行x的另一个同步方法。

   B线程在同步方法中执行x.notify () 方法时,jvm 把A线程从x对象的等待池中移动到x对象的锁池中,等待获取锁。

   B线程执行完同步方法,释放A线程获取锁,继续上面流程。

修改 SharedResource.java

package com._520it.day02._producer_consumer; // 共享的资源public class ShapedResource { private String name ;  private String gender ;  private boolean isEmpty = true;   // 表示共享资源对象是否为空  // 生产者生产资源  // synchroize 可以理解为线程互斥实现同步,在该线程进入资源区,其他线程只能等  synchronized public void push(String name , String gender ) {  try {   while(!isEmpty){ // 当不空时 ,等消费者来获取数据     this.wait();  // 生产完了,释放锁    // 使用同步锁对象来调用,表示当前进程释放同步锁,进入等待池   }  // 生成开始   this.name = name ;    Thread.sleep(10);   this.gender = gender ;   this.notify(); // 唤醒一个消费者   isEmpty = false ; // 现在共享资源中数据不为空了  }  catch (InterruptedException e) {   e.printStackTrace();  } } // 消费者消费资源 synchronized public void pop() {  try {   while(isEmpty) {    this.wait();   }   // 消费开始   System.out.println(this.name +"-"+this.gender);   Thread.sleep(10);   // 消费结束   this.notify(); // 唤醒一个生产者   isEmpty = true ;   } catch (InterruptedException e) {     e.printStackTrace();  }  } }

package com._520it.day02._producer_consumer; // 测试类public class App { public static void main(String[] args) { // 共享资源 ShapedResource resource = new ShapedResource() ; new Thread(new Producer(resource)).start(); new Thread(new Consumer(resource)).start(); }}

线程通信 -使用 lock 和Condition 接口 

wait和notify 方法只能被同步监听锁对象来调用,否则报错,那么,lock 机制根本没有同步锁,也就没有自动获取锁和自动释放锁的概念。因为没有同步锁,所以lock机制不能调用 wait 和 notify 方法。

解决方案 : java5中提供了 lock 机制的同时提供了处理lock机制的通信控制的 Condition 接口。

使用Lock 机制可以取代 synchroized 代码块和 synchroized 修饰

使用 Condition 接口对象的await , signal,signalAll 方法取代 Object 类中的 wait , notify ,notifyAll 方法。

package com._520it.day02._02_producer_consumer_lock; import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; // 共享的资源public class ShapedResource { private String name ;  private String gender ;  private boolean isEmpty = true;   private final Lock lock = new ReentrantLock()  ; private Condition condition = lock.newCondition() ;  // 表示共享资源对象是否为空  // 生产者生产资源  // synchroize 可以理解为线程互斥实现同步,在该线程进入资源区,其他线程只能等   public void push(String name , String gender ) {     lock.lock(); // 加锁   try {    while(!isEmpty) {     condition.await();    }   this.name = name ;    Thread.sleep(10);   this.gender = gender ;   isEmpty = false ;    condition.signalAll();  }  catch (InterruptedException e) {   e.printStackTrace();  }finally {   lock.unlock(); // 释放锁  } } // 消费者消费资源  public void pop() {   lock.lock(); // 加锁  try {   while(isEmpty) {     condition.await();    }   // 消费开始   System.out.println(this.name +"-"+this.gender);   Thread.sleep(10);   // 消费结束   isEmpty = true;     condition.signalAll();   } catch (InterruptedException e) {   e.printStackTrace();  }  finally {   lock.unlock();  }  } }

线程的生命周期

线程的生命周期分为5个部分:分别是新建状态、就绪状态、运行状态,阻塞状态、死亡状态。

新建状态(new):指新建了一个线程对象。Thread    t1 =new Thread();这里就新建了一个Thread类的线程对象。

就绪状态(Runnable):当线程对象创建后,该线程对象自身或者其他对象调用了该对象的start()方法。该线程就位于了可运行池中,变的可运行,等待获取cpu的使用权。因为在同一时间里cpu只能执行某一个线程。

运行状态(Running): 当就绪状态的线程获取了cpu的时间片或者说获取了cpu的执行时间,这时就会调用该线程对象的run()方法,然后就从就绪状态就入了运行状态。

阻塞状态(Blocked):阻塞状态就是线程因为某种原因暂时放弃了对cpu的使用权,暂时停止运行。直到线程再次进入就绪状态,才有机会转到运行状态。阻塞状态分为三种情况:

等待阻塞:运行状态的线程调用了wait()方法后,该线程会释放它所持有的锁,然后被jvm放入到等待池中,只有等其他线程调用Object类的notify()方法或者norifyAll()方法时,才能进入重新进入到就绪状态。

同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,JVM就会把该线程设置为阻塞状态,一直到线程获取到同步锁,才能转入就绪状态。

其它阻塞:运行的线程在执行sleep()或者join()方法时,或者发出了I/O请求,JVM就会把该线程设置为阻塞状态,当sleep()状态超时、join()等待等待线程终止或者超时、或者I/O处理完毕时,线程重进转入到就绪状态。在这需要注意的是sleep()方法和wait()不同,sleep不会释放自身所持有的锁。

死亡状态(Dead):当线程执行完了或者因异常退出了run()的执行,该线程的生命周期就结束了。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多