多线程之线程的状态和通信

线程调试工具:ThreadDump

线程的五种状态:

1.新建(new):
新建了一个线程对象.
2.可运行(runnable):
线程对象创建后,其他线程(比如mian线程)调用了该对象的start方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权
3运行(running):
可运行状态的线程获得了cpu时间片,执行程序代码
4.阻塞(blocked):
阻塞状态是指线程因为某种原因放弃了cpu的使用权,也让出了cpu时间片,暂时停止运行,直到线程进入可运行状态,才有机会再次获得CPU 时间片转到运行状态
5.死亡(dead):
线程run main方法执行结束,或者run方法出现异常,该线程的生命周期结束,死亡线程不可复生

三种阻塞:
1.等待阻塞:
运行的线程使用的wait方法,JVM将该线程放进等待队列中
2.同步阻塞:
运行线程获取对象同步锁的时候,若该线程锁被别的线程所占用,JVM会把该线程放入锁池中
3.其他阻塞;
执行了sleep join等方法,或者发出了IO请求,当sleep超时,join等待线程终止或者超时,io处理完毕时,线程会重新转入可运行状态

 

 

1.sleep()和yield()和join()

1)sleep()方法作用:让当前线程睡眠一段时间,期间不会释放任何持有的锁。

2) yield()方法作用:让出该线程的时间片给其它线程。线程调用了yield()方法,表示放弃当前获得的CPU时间片,回到就绪状态。最后由线程调度重新选择就绪状态的线程分配CPU资源。

3)join()方法作用:暂停当前线程,等待被调用线程指向结束之后再继续执行。
 

线程的通信有两种方式:
1.synchronized + wait + notify+notifyAll:

四个方法都是object的方法,所以所有类都可以继承这四个方法
wait方法:
使得当前线程必须要等待,等到另一个线程调用notify或者notifyall方法.
notify,notifyAll:
会唤醒一个等待当前对象的锁的线程,而notifyAll就是唤醒所有等待锁的线程.
wait notify方法要求在调用时线程已经获得对象的锁,所以这两个方法要放在synchronized方法或者synchronized块中

sleep与wait方法的区别:sleep方法是不释放锁的,wait方法释放锁
2.lock + condition + await + signal:
都可以达到通信的效果,但是对比第一种方式:
1.locl是个类
2.执行完必须在finally里释放锁,执行前必须上锁,而且要紧跟try代码块
3.可以自己控制锁哪个线程,保证公平性
4.性能上高一些,因为大量同步 

例子:

  两个线程,依次执行打印奇数和偶数。
    
     因为wait() notifyAll() 必须是同一对象的多个线程之间的通信,所以有如下程序:
package thread_Runnable;

class Printer {

    int i = 1;

    synchronized public void  printEven(){
        for(;i<=100;) {
            if (i % 2 == 1) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " is printting i: " + i++);
                notify();
            }
        }
    }

    synchronized public void printOdd(){
        for(; i<=100; ) {
            if (i % 2 == 0) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " is printting i: " + i++);
                notify();
            }
        }
    }

}

// 必须要创建两个类 去执行对应的方法,才能保证唤醒另一个线程  要实现两个线程分别打印,所以有如下代码:
class PrintEven implements Runnable{
    Printer printer;
    public PrintEven(Printer printer){
        this.printer = printer;
    }
    public void run() {
        printer.printEven();

    }
}

 class PrintOdd implements Runnable {
    Printer printer;
    public PrintOdd(Printer printer){
        this.printer = printer;
    }
    public void run() {
        printer.printOdd();

    }
}
//主函数: 把printer作为对象传入
class main{
    public  static void main(String[] args){
        
        Printer printer = new Printer();
        
        Runnable printOdd = new PrintOdd(printer);
        Thread threadOdd = new Thread(printOdd, "thread1");

        Runnable printEven = new PrintEven(printer);
        Thread threadEven = new Thread(printEven, "thread2");

        threadOdd.start();
        threadEven.start();
    }
}

 

注意:

◆调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {……} 代码段内。

◆调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {……} 代码段内唤醒A.

◆当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

◆如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。

◆obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

◆当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。

原文链接:https://blog.csdn.net/weixin_43326401/article/details/104107084

https://www.cnblogs.com/shirley18/p/9705729.html

posted @ 2020-03-11 16:58  你猜lovlife  阅读(178)  评论(0)    收藏  举报