并发编程的口诀
- 线程 操作 资源类
- 判断 干活 通知
- 注意虚假唤醒
synchronized版本的 多生产 多消费注意的问题
- if判断标记,只会判断一次,会导致不该运行的线程运行了,导致数据错误,while判断标记,解决的线程获取到执行权限之后是否要执行的问题。
- notify只会唤醒一个线程,如果是唤醒本方线程就会没有意义,while判断标记 + notify 会导致死锁,notifyAll解决了本方线程一定会唤醒对方线程问题;
wait()和sleep()区别?
- 使用方式
wait()可以设置时间,也可以不设置时间,sleep()必须设置时间 - 对锁和执行权的处理
wait: 释放执行权限,释放锁
sleep:释放执行全县,不释放锁
测试代码
@Test
public void testSynchronize() throws InterruptedException {
Resource resource = new Resource();
Producer produceTask = new Producer(resource);
new Thread(produceTask,"producer A.... ").start();
new Thread(produceTask,"producer B.... ").start();
Consumer consumer = new Consumer(resource);
new Thread(consumer,"consumer C.... ").start();
new Thread(consumer,"consumer D.... ").start();
Thread.sleep(10000);
}
共享的资源类
static class Resource {
private String name;
private int count = 1;
private boolean flag = false;
private final Object lock = new Object();
public void set(String name) {
synchronized (this.lock) {
while(flag) { //这个地方必须用while,否则,wait线程被唤醒的时候,不会再次判断flag,导致还没有消费,又生产新的;
try { this.lock.wait(); } catch (InterruptedException ignored) { }
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName() + " ...." + " 生产者 " + this.name);
flag = true; // 设置flag和唤醒操作不能交换,可以思考了一下,如果交换会发生什么情况
this.lock.notifyAll(); //这个地方一定要用notifyAll,否则可能会发生 没有唤醒消费者的线程(虚假唤醒)
}
}
public void out() {
synchronized (this.lock) {
while(!flag) { // 同上
try { this.lock.wait(); } catch (InterruptedException ignored) {}
}
System.out.println(Thread.currentThread().getName() + " ...." + " 消费者 " + this.name);
flag = false;
this.lock.notifyAll();
}
}
}
生产者
static class Producer implements Runnable{
private final Resource resource;
public Producer(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
for(int i = 0;i < 100;i++){
resource.set("馒头");
}
}
}
消费者
static class Consumer implements Runnable{
private final Resource resource;
public Consumer(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
for(int i = 0;i < 100;i++){
resource.out();
}
}
}