1. wait和notify
1.1 使用场景
- 使用场景1:线程1
list
个数不等于5,就wait
;线程2添加元素,尽管个数大于5,notify
,但线程2继续持有锁,不会释放,所以线程1等线程2执行完才会输出
public class MyContainer2 {
//添加volatile,使lists在其他线程可见
volatile List lists=new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer2 c=new MyContainer2();
final Object lock=new Object();
new Thread(()->{
synchronized (lock) {
System.out.println("t1 start");
if(c.size()!=5) {
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("t1 end");
}
},"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
new Thread(()->{
System.out.println("t2 start");
synchronized (lock) {
for(int i=0;i<10;i++) {
c.add(new Object());
System.out.println("add "+i);
if(c.size()>5) {
lock.notify();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("t2 end");
},"t2").start();
}
}
t1 start
t2 start
add 0
add 1
add 2
add 3
add 4
add 5
add 6
add 7
add 8
add 9
t2 end
t1 end
- 使用场景2:让线程2先释放锁,线程1执行完,再让线程2执行
public class MyContainer3 {
//添加volatile,使lists在其他线程可见
volatile List lists=new ArrayList();
public void add(Object o) {
lists.add(o);
}
public int size() {
return lists.size();
}
public static void main(String[] args) {
MyContainer3 c=new MyContainer3();
final Object lock=new Object();
new Thread(()->{
synchronized (lock) {
System.out.println("t1 start");
if(c.size()!=5) {
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("t1 end");
//通知t1继续执行
lock.notify();
}
},"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
new Thread(()->{
System.out.println("t2 start");
synchronized (lock) {
for(int i=0;i<10;i++) {
c.add(new Object());
System.out.println("add "+i);
if(c.size()==5) {
lock.notify();
//让t2释放锁,让t1执行
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("t2 end");
},"t2").start();
}
}
1.2 原理
使用 wait() 和 notify() 一定要放在同步代码块中,关联一个监视器对象,被 wait() 的线程会被记录到 Wait Set 等待队列中,被阻塞的线程会记录到 Entry Set 阻塞队列中
使用 notifyAll() :Wait Set 中被唤醒的线程会与 Entry Set 中被唤醒的线程以及其他(可能的)活跃线程共同参与抢夺锁资源。如果其中一个被唤醒的等待线程成功申请到锁,那么该线程就会从 Wait Set 中移除。否则,这些被唤醒的线程仍然停留在 Wait Set 中,并再次被暂停,以等待下次申请锁的机会。
public class Object {
//...
public final void wait() throws InterruptedException {
wait(0);
}
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
}
2. await 和 signal
2.1 使用场景
public class MyContainer3 {
final Lock lock = new ReentrantLock();
// condition 依赖于 lock 来产生
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
// 生产
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await(); // 队列已满,等待,直到 not full 才能继续生产
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal(); // 生产成功,队列已经 not empty 了,发个通知出去
} finally {
lock.unlock();
}
}
// 消费
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await(); // 队列为空,等待,直到队列 not empty,才能继续消费
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal(); // 被我消费掉一个,队列 not full 了,发个通知出去
return x;
} finally {
lock.unlock();
}
}
}
2.2 原理
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
public interface Condition {
//...
void await() throws InterruptedException;
void signal();
}