开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
本文主要讲解内容:并发中的生产者消费者问题;8种锁现象。
4.生产者消费者问题
Synchronized实现的生产者消费者问题:实现的效果就是A线程执行一次+1,B线程执行一次-1
class Data {
private int number = 0;
//+1 -1
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>"+ number);
//通知其他线程,+1完毕了
this.notify();
}
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>"+ number);
//通知其他线程,-1完毕了
this.notify();
}
}
这部分代码有个问题:线程虚假唤醒(虚假唤醒就是在多线程执行过程中,线程间的通信未按照我们幻想的顺序唤醒,故出现数据不一致等不符合我们预期的结果。)
基于Synchronized实现的改成Lock实现。
-
第一步:
new ReentranLock(),在代码中lock.lock()加锁 -
第二步:try...catch...finally捕获异常(代码业务逻辑写到try,异常捕获到catch,释放锁在finally中进行)
-
第三步:替换synchronized中的wait和notify方法
- 具体方法:需要
lock.newCondition();造一个Condition对象,也就是lock里面的对象监视器 - 使用Condition中的await方法代替wait方法让线程睡眠;使用signalAll方法替代notify唤醒线程。
- 具体方法:需要
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//+1 -1
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>"+ number);
//通知其他线程,+1完毕了
condition.signalAll();
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
【注意】:只是这样做会存在一个问题,无法做到我们的需求:A执行完后执行B再执行C再执行D。Synchorized无法实现这种需求。
可以用多个Condition对象来解决这个问题,如下:
condition1.await();第一个线程处于睡眠 condition2.signal();第二个线程被唤醒
class Data3 {
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
int number = 1;//1A 2B 3C
public void printA() {
lock.lock();
try {
while (number != 1){
condition1.await();
}
System.out.println(Thread.currentThread().getName() +"AAAA");
//唤醒指定线程
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() +"BBBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() +"CCCC");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
5. 8锁现象理解什么是锁
如何判断锁的是谁?知道什么锁,锁的谁?
sycchronized锁的对象是方法的调用者。 static锁的是Class。
标准情况:锁的是用一个对象(对象只有一个),谁先拿到谁就先执行。如果是两个phone对象的话也是互不影响的,毕竟是两把锁了。
加入了普通方法:相当于一个上锁了一个没上锁,那并不影响普通方法的执行
增加两个静态的同步方法,两个对象,但是静态static锁的是class,所以还是一把锁。
1个静态同步方法,一个普通同步方法,一个对象,是两把锁的情况。
其实只要弄清是几把锁,锁的谁,那就一目了然了。