JAVASE线程---线程间通信

Wait    :冻结线程,释放资源,释放锁

notify  :当线程被冻结时,线程将处于线程池(系统在内存中给分的一块空间),

执行notify后,唤醒线程池中的第一个被等待的线程

notifyAll : 唤醒线程池中所有被等待的线程


上述都使用在同步中,因为要对持有监视器()的线程操作,所以要使用在同步中,因为只有同步才具有锁

 这些方法在操作同步线程,都必须要标识它们所操作线程仅有的锁

只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒 

也就是说,等待和唤醒必须是同一个锁


这些方法都之所以都被定义在Object类中:

锁可以是任意对象,可以被任意对象调用的方法定义在Object类中


线程间通讯:

其实就是多个线程在操作同一个资源

但操作动作不同

可以简单的想象成:一个输入线程和一个输出线程在操作一个文件(缓冲区)

也类似与操作系统中的生产者和消费者问题


以上的程序仅适合于两个线程时,当线程大于两个时候,该程序会出现错误

假设此时有四个线程:t1,t2,t3,t4,flag此时为false

程序首先设置输入资源和输出资源是同一个资源,即同一个Resource

假设t1,先抢到执行权,进入set同步函数,flag为假,生产一个产品后,flag被置为true,冻结,t1释放执行权,

此时假设t2抢得执行权,由于flagtrue,直接被冻结,t2释放执行权

假设t3抢到执行权,进入out,判断flagtrue,正常消费一个产品后设置flag,并唤醒线程池中第一个线程即t1,并冻结自己,t3释放执行权

假设此时t4抢到执行权,进入后flagfalse直接冻结

此时程序中苏醒的线程有t1

t1此时抢到执行权,再次生产产品,然后设置flag,唤醒t2,但是t2是生产方的线程,就会出现问题

假设t2抢到执行权,此时t2不判断flag,直接生产产品(注意此处:t1第一次生产的产品没有被消费),完成后唤醒t3

t3拿到执行权后,消费t2 生产的那个,t1生产的没有被执行。

产生的原因是:

t2醒的时候没有判断标记flag,当将if变成while后,可判断多次,直到flag不满足条件

但是会引发另外一个问题:

t2被唤醒的时候,去判断标记,flagtrue,然后继续等待,

此时四个线程全部等待,死锁发生。

 

出现的问题的根本原因是:

唤醒的时候应该唤醒对方的线程,但是却唤醒了本方的线程,所以造成了错误

所以可以将notify改为notifyAll,将所有的都唤醒,本方的就会循环判断标记,然后就会继续等待。


生产者与消费者的改进版:

java.util.concurrent.locks 中定义了接口Condition和接口Lock,显示的锁机制

javaAPI中是这样解释的:

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 setwait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

 

获得锁的时候需要显示的用lock方法获得,用unlock方法显示释放锁

await方法用于等待线程,signal唤醒一个等待线程,signalAll唤醒一个所有的等待线程

 

注意: 

    这些只有在jdk1.5版本以后提供, 将同步Synchronized替换成显示lock操作

    将Object中的waitnotifynotifyAll,替换了Condition对象

    该对象可以Lock锁,进行获取,可以再该实例中,实现本方只唤醒对方操作

    因为一个锁可以创建多个Condition

 

修正后的生产者与消费者程序



你可能感兴趣的:(JAVASE线程---线程间通信)