1.使用Object.wait()
,Object.notify()
,Object.notifyAll()
。
wait()
方法会使当前线程进入阻塞/睡眠状态,等待其他线程唤醒。并释放对这个对象的锁。,使用该方法需要同synchronized关键字配合使用,否则会抛出一个IllegalMonitorStateException异常信息。意思是当前线程必须要锁定这个对象后,才能使用这个对象的wait()方法。必须先获得对这个对象的访问权限也就是锁。notify()
唤醒等待对这个对象的访问的线程。如果有多个的话,则唤醒最先等待的线程。同样需要和synchronized关键字配合使用。notifyAll()
唤醒所有等待对这个对象的访问的线程,但该方法并不能明确唤醒的顺序。
public class Test9 {
public static Object object = new Object();
public static void main(String[] args) {
Test9 test9 = new Test9();
MyThread1 myThread1_1 = test9.new MyThread1("线程1-1");
myThread1_1.start();
MyThread1 myThread1_2 = test9.new MyThread1("线程1-2");
myThread1_2.start();
MyThread1 myThread1_3 = test9.new MyThread1("线程1-3");
myThread1_3.start();
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyThread2 myThread2 = test9.new MyThread2();
myThread2.start();
}
class MyThread1 extends Thread {
public MyThread1(String name){
super(name);
}
@Override
public void run() {
synchronized (object) {
try {
System.out.println(Thread.currentThread().getName()+"阻塞等待,时间:"+System.currentTimeMillis());
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
object.notify();
}
System.out.println(Thread.currentThread().getName()+"该线程被唤醒了,并拥有对object的锁.时间:"+System.currentTimeMillis());
}
}
}
class MyThread2 extends Thread {
@Override
public void run() {
synchronized(object){
object.notify();
}
}
}
}
---sout--- 可以看出在8秒后线程1_1被线程二唤醒了。
线程1-1阻塞等待,时间:1607563740121
线程1-3阻塞等待,时间:1607563740121
线程1-2阻塞等待,时间:1607563740121
线程1-1该线程被唤醒了,并拥有对object的锁.时间:1607563748123
//当把线程二改为唤醒全部
class MyThread2 extends Thread {
@Override
public void run() {
synchronized(object){
object.notifyAll();
}
}
}
---sout--- 可以看出在8秒后线程1的所有被线程二唤醒了,但并没有按先入先出。
线程1-1阻塞等待,时间:1607563934233
线程1-3阻塞等待,时间:1607563934234
线程1-2阻塞等待,时间:1607563934234
线程1-2该线程被唤醒了,并拥有对object的锁.时间:1607563942236
线程1-3该线程被唤醒了,并拥有对object的锁.时间:1607563942236
线程1-1该线程被唤醒了,并拥有对object的锁.时间:1607563942236
2.模拟生产-消费模型
当生产者在生产过程中消费者一直处于等待中,而当消费者在消费中生产者处于等待状态。也就是两者交换对临界资源的占有权。
public class Test2 {
private static int queueSize = 5;
//定义一个存放产品的容器队列
private static PriorityQueue<String> producerQueue = new PriorityQueue(queueSize);
public static void main(String[] args) {
Test2 test = new Test2();
Producer producer = test.new Producer("-生成者-");
Consumer consumer = test.new Consumer("-消费者-");
consumer.start();
producer.start();
}
class Consumer extends Thread {
public Consumer(String name) {
super(name);
}
@Override
public void run() {
while (true) {
synchronized (producerQueue) {
//判断是否还有产品
if (producerQueue.size() == 0) {
try {
System.out.println(Thread.currentThread().getName() + "仓库已空,等待生产者生产产品");
//通知生产者
producerQueue.notify();
//使当前线程阻塞 释放对queue对象的锁,等待被唤醒
producerQueue.wait();
} catch (InterruptedException e) {
producerQueue.notify();
}
}
producerQueue.poll();
System.out.println(Thread.currentThread().getName() + "取走一个产品,仓库还剩" + producerQueue.size() + "个产品");
}
}
}
}
class Producer extends Thread {
public Producer(String name) {
super(name);
}
@Override
public void run() {
while (true) {
synchronized (producerQueue) {
//判断仓库是否已满
if (producerQueue.size() == queueSize) {
try {
System.out.println(Thread.currentThread().getName() + "仓库已满,等待消费者消费完毕!");
//通知消费者
producerQueue.notify();
//使当前线程阻塞 释放对queue对象的锁,等待被唤醒
producerQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
producerQueue.notify();
}
}
producerQueue.offer("包子");
System.out.println(Thread.currentThread().getName() + "生产一个产品,放进仓库。产品个数:" + producerQueue.size());
}
}
}
}
}
---sout---
-消费者-仓库已空,等待生产者生产产品
-生成者-生产一个产品,放进仓库。产品个数:1
-生成者-生产一个产品,放进仓库。产品个数:2
-生成者-生产一个产品,放进仓库。产品个数:3
-生成者-生产一个产品,放进仓库。产品个数:4
-生成者-生产一个产品,放进仓库。产品个数:5
-生成者-仓库已满,等待消费者消费完毕!
-消费者-取走一个产品,仓库还剩4个产品
-消费者-取走一个产品,仓库还剩3个产品
-消费者-取走一个产品,仓库还剩2个产品
-消费者-取走一个产品,仓库还剩1个产品
-消费者-取走一个产品,仓库还剩0个产品
-消费者-仓库已空,等待生产者生产产品
-生成者-生产一个产品,放进仓库。产品个数:1
-生成者-生产一个产品,放进仓库。产品个数:2
-生成者-生产一个产品,放进仓库。产品个数:3
-生成者-生产一个产品,放进仓库。产品个数:4
-生成者-生产一个产品,放进仓库。产品个数:5
-生成者-仓库已满,等待消费者消费完毕!
3. jdk1.5版本Lock中引入了Condition
-
Condition
是一个顶层接口,其中定义了如await()、await(long time, TimeUnit unit)
、signal()、signalAll()
等方法,其作用和Object中的方法类似。 -
官方在描述Condition引入了一个
BoundedBuffer
栗子模拟了生产者消费模式,通过lock中提供的方法newCondition()
创建Condition,在栗子中可以看出put()、take()
在对临界资源items操作时同样需要先获取到该对象锁。class BoundedBuffer { final Lock lock = new ReentrantLock(); 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(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { //必须在此手动释放锁,否则异常后会造成死锁 lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
4.阻塞队列BlockingQueue
。
*take(),put()
方法就是通过Condition 来进行相互通知。
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
因此本身阻塞队列就实现了生成消费模式。