多线程的通信

一、线程共包括以下5种状态。

1. 新建状态(New) 

线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。

2. 就绪状态(Runnable)

也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

3. 运行状态(Running) 

线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

4. 阻塞状态(Blocked) 

阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(1)等待阻塞 :通过调用线程的wait()方法让线程等待某工作的完成。

(2)同步阻塞 :线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。

(3)其他阻塞 :通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5.死亡状态(Dead) 

线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

二、理解moniter的概念。

关于什么是moniter?

这个名词如果第一次听说,可能会比较迷。暂且可以理解为java虚拟机给每个对象的class字节码都设置了一个监听器Monitor

如果有了解过synchronized底层的实现方式,应该会对entry-setwait-set这两个名词比较熟悉。

       我们可以把Monitor想象成一个保险箱,里面专门存放一些需要被保护的数据。Monitor每次只允许一个线程进入,当一个线程需要访问受保护的数据(即需要获取对象的Monitor)时,它会首先在entry-set入口队列中排队(这里并不是真正的按照排队顺序),如果没有其他线程正在持有对象的Monitor,那么它会和entry-set队列和wait-set队列中的被唤醒的其他线程进行竞争(即通过CPU调度),选出一个线程来获取对象的Monitor,执行受保护的代码段,执行完毕后释放Monitor,如果已经有线程持有对象的Monitor,那么需要等待其释放Monitor后再进行竞争。

     再说一下wait-set队列。当一个线程拥有Monitor后,经过某些条件的判断(比如用户取钱发现账户没钱),这个时候需要调用Objectwait方法,线程就释放了Monitor,进入wait-set队列,等待Objectnotify方法(比如用户向账户里面存钱)。当该对象调用了notify方法或者notifyAll方法后,wait-set中的线程就会被唤醒,然后在wait-set中被唤醒的线程和entry-set中的线程一起通过CPU调度来竞争对象的Monitor,最终只有一个线程能获取对象的Monitor

三、讲解wait和notify,notifyall

wait()

      使用wait方法会让当前线程处于一个等待的装填,知道另外的线程调用notify或者notifyAll之后才会唤醒。在等待期间,该线程必须要放弃掉自身的moniter,(这里是强制释放锁,与sleep正好相反,sleep会占用moniter),然后该线程会被放入到等待队列中,直到接收到notify或者notifyall指令才会被唤醒。

notify()

     使用notify方法可以将等待队列中的正在等待这个对象的moniter的线程唤醒,将其加入到同步队列中进行moniter的抢夺。

notifyAll()

    调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

       在旧版本的jdk中,线程之间的通信主要局限于Object类里面的notify和wait等系列方法,wait函数不能够单独使用,必须是在synchronized 下才能使用,而且wait()必须要通过notify()或者notifyAll()的方法才能进行唤醒,并且wait-set只有一个,过于单一,因此缺乏一定的灵活性。

      除了使用wait,notify,notifyall之外来实现线程的唤醒和阻塞之外,还有其他的工具类可以提供类似的功能。例如说LockSupport里面的park和unpark函数,可以提供对于线程状态的控制功能。

import java.util.concurrent.locks.LockSupport;
 
 /**
  * @author idea
  * @data 2019/3/17
  */
 class ParkThread implements Runnable{
 
 
    @Override
    public void run() {
        System.out.println("ParkThread is run");
        //堵塞当前线程!
        LockSupport.park();
        System.out.println("ParkThread is over");
    }
}

class UnParkThread implements Runnable{

    private Thread thread;

    public UnParkThread(Thread thread) {
        this.thread = thread;
   }

    @Override
    public void run() {
        System.out.println("UnParkThread is run");
        //解除堵塞该线程!
        LockSupport.unpark(thread);
        System.out.println("UnParkThread is over");
    }
}

public class LockSupportDemo {

   public static void main(String[] args) {
        Thread parkThread=new Thread(new ParkThread());
        System.out.println("-----------");
        Thread unparkThread=new Thread(new UnParkThread(parkThread));
        parkThread.start();
        unparkThread.start();
    }

}

Condition

Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,有了解过阻塞队列底层实现的朋友应该会知道,阻塞队列实际上是使用了Condition来模拟线程间协作。

  • Condition是个接口,基本的方法就是await()和signal()方法

  • Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition() 

  • 调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用

  • Conditon中的await()对应Object的wait();

  • Condition中的signal()对应Object的notify();

  • Condition中的signalAll()对应Object的notifyAll()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值