synchronized版
使用if判断(会出现虚假唤醒)
只有两个线程的情况
public class SynchronizedPC {
public static void main(String[] args) {
Date date = new Date();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.subtract();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//资源类
class Date{
private int number = 0;
public synchronized void add() throws InterruptedException {
if (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "——>" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
public synchronized void subtract() throws InterruptedException {
if (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "——>" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
多个线程的情况
public class SynchronizedPC {
public static void main(String[] args) {
Date date = new Date();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.subtract();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.subtract();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//资源类
class Date{
private int number = 0;
public synchronized void add() throws InterruptedException {
if (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "——>" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
public synchronized void subtract() throws InterruptedException {
if (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "——>" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
存在问题:如果是多个线程(可能不会出现上面预期的结果,可能是有2,3出现) ——> 虚假唤醒
使用while循环
为解决虚假唤醒问题我们需要将if判断改为while循环
用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
拿两个加法线程A、C来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程C获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后C再执行。如果是if的话,那么A修改完num后,C不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,C还会去判断num的值,因此就不会执行。
public class SynchronizedPC {
public static void main(String[] args) {
Date date = new Date();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.subtract();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
date.subtract();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//资源类
class Date{
private int number = 0;
public synchronized void add() throws InterruptedException {
while (number!=0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "——>" + number);
//通知其他线程,我+1完毕了
this.notifyAll();
}
public synchronized void subtract() throws InterruptedException {
while (number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "——>" + number);
//通知其他线程,我-1完毕了
this.notifyAll();
}
}
Lock版
生产者和消费者问题 JUC版本
通过Lock找到Condition
代码实现
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockPC {
public static void main(String[] args) {
Date2 date = new Date2();
new Thread(()->{
for (int i = 0; i < 5; i++) {
date.add();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
date.subtract();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
date.add();
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
date.subtract();
}
},"D").start();
}
}
//资源类
class Date2{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition =lock.newCondition();
//condition.await(); 等待
//condition.signalAll(); 唤醒全部
public void add(){
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();
}
}
public void subtract(){
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();
}
}
}
Condition实现精准通知唤醒
可以实现A执行完,调用B,B执行完,调用C,C执行完,调用A的执行顺序
代码实现
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionTest {
public static void main(String[] args) {
//A执行完,调用B,B执行完,调用C,C执行完,调用A
Date3 date = new Date3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
date.printC();
}
},"C").start();
}
}
//资源类 Lock
class Date3{
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; //A=1,B=2,C=3
public void printA(){
lock.lock();
try {
while (number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "——> AAAAA");
number = 2;
//唤醒线程B
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() + "——> BBBBB");
number = 3;
//唤醒线程C
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() + "——> CCCCC");
number = 1;
//唤醒线程A
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}