Wait :冻结线程,释放资源,释放锁
notify :当线程被冻结时,线程将处于线程池(系统在内存中给分的一块空间),
执行notify后,唤醒线程池中的第一个被等待的线程
notifyAll : 唤醒线程池中所有被等待的线程
上述都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁
这些方法在操作同步线程,都必须要标识它们所操作线程仅有的锁
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒
也就是说,等待和唤醒必须是同一个锁
这些方法都之所以都被定义在Object类中:
锁可以是任意对象,可以被任意对象调用的方法定义在Object类中
线程间通讯:
其实就是多个线程在操作同一个资源
但操作动作不同
可以简单的想象成:一个输入线程和一个输出线程在操作一个文件(缓冲区)
也类似与操作系统中的生产者和消费者问题
以上的程序仅适合于两个线程时,当线程大于两个时候,该程序会出现错误
假设此时有四个线程:t1,t2,t3,t4,flag此时为false
程序首先设置输入资源和输出资源是同一个资源,即同一个Resource
假设t1,先抢到执行权,进入set同步函数,flag为假,生产一个产品后,flag被置为true,冻结,t1释放执行权,
此时假设t2抢得执行权,由于flag为true,直接被冻结,t2释放执行权
假设t3抢到执行权,进入out,判断flag为true,正常消费一个产品后设置flag,并唤醒线程池中第一个线程即t1,并冻结自己,t3释放执行权
假设此时t4抢到执行权,进入后flag为false直接冻结
此时程序中苏醒的线程有t1,
t1此时抢到执行权,再次生产产品,然后设置flag,唤醒t2,但是t2是生产方的线程,就会出现问题
假设t2抢到执行权,此时t2不判断flag,直接生产产品(注意此处:t1第一次生产的产品没有被消费),完成后唤醒t3,
t3拿到执行权后,消费t2 生产的那个,t1生产的没有被执行。
产生的原因是:
t2醒的时候没有判断标记flag,当将if变成while后,可判断多次,直到flag不满足条件
但是会引发另外一个问题:
当t2被唤醒的时候,去判断标记,flag为true,然后继续等待,
此时四个线程全部等待,死锁发生。
出现的问题的根本原因是:
唤醒的时候应该唤醒对方的线程,但是却唤醒了本方的线程,所以造成了错误
所以可以将notify改为notifyAll,将所有的都唤醒,本方的就会循环判断标记,然后就会继续等待。
生产者与消费者的改进版:
在java.util.concurrent.locks 中定义了接口Condition和接口Lock,显示的锁机制
javaAPI中是这样解释的:
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
获得锁的时候需要显示的用lock方法获得,用unlock方法显示释放锁
await方法用于等待线程,用signal唤醒一个等待线程,signalAll唤醒一个所有的等待线程
注意:
这些只有在jdk1.5版本以后提供, 将同步Synchronized替换成显示lock操作
将Object中的wait,notify,notifyAll,替换了Condition对象
该对象可以Lock锁,进行获取,可以再该实例中,实现本方只唤醒对方操作
因为一个锁可以创建多个Condition
修正后的生产者与消费者程序