在生产者与消费者关系中,存在着一个多线程操作同一个资源的情况,在这里我使用了int作为资源类型,而++操作不是线程安全的,因此会导致数据错误。而为了保证线程执行的有序性,急需要对方法进行加锁。
一:使用synchronized锁
//生产者与消费者
public class ProductCustomer {
public static void main(String[] args) {
Data d =new Data();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
d.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadA ").start();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
d.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadB ").start();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
d.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadC ").start();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
d.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadD ").start();
}
}
class Data{
private int data = 10;
public synchronized void increment() throws InterruptedException {
//这里要使用while而不能是if,因为if只会判断一次,一旦多个线程同时操作则会出错
//因此可能会导致wait的虚假唤醒
while (data!=10){
this.wait(); //等待
}
data++;
System.out.println(Thread.currentThread().getName()+"=>"+data);
//通知线程++完毕
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while(data==10){
this.wait(); //等待
}
data--;
System.out.println(Thread.currentThread().getName()+"=>"+data);
//通知线程--完毕
this.notifyAll();
}
}
二:使用Lock锁
//JUC生产者与消费者
public class NewProductCustomer {
public static void main(String[] args) {
Customer c = new Customer();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
c.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadA ").start();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
c.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadB ").start();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
c.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadC ").start();
new Thread(()->{for(int i=1;i<=10;i++) {
try {
c.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"threadD ").start();
}
}
class Customer{
private int datas = 10;
Lock lock = new ReentrantLock();//声明锁
Condition condition = lock.newCondition();//对象监视器
public void increment() throws InterruptedException {
lock.lock();
try {
while (datas!=10){
condition.await();//等待
}
datas++;
System.out.println(Thread.currentThread().getName()+"=>"+datas);
condition.signalAll();//通知线程++完毕
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while(datas==10){
condition.await(); //等待
}
datas--;
System.out.println(Thread.currentThread().getName()+"=>"+datas);
condition.signalAll(); //通知线程--完毕
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
三:使用Lock锁配置多监视器
上面使用lock锁的模式虽然也能在两条线程的情况下保证一个操作的有序性,但一旦线程数达到三条或者更高就会出错,因此需要制定多个监视器来对不同的线程进行一个更加精确的有序唤醒。
//使用多个监视器进行有序唤醒
public class NewProductCustomer2 {
public static void main(String[] args) {
Customer2 c = new Customer2();
new Thread(()->{for(int i=1;i<=10;i++)c.A();},"Thread01 ").start();
new Thread(()->{for(int i=1;i<=10;i++)c.B();},"Thread02 ").start();
new Thread(()->{for(int i=1;i<=10;i++)c.C();},"Thread03 ").start();
}
}
class Customer2{
private int data3 = 1;
private Lock lock = new ReentrantLock();//获取lock锁
Condition condition1 = lock.newCondition();//监视器
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void A(){
lock.lock();
try {
//while防止虚假唤醒
while(data3!=1){
condition1.await();//线程等待
}
data3 = 2;
System.out.println(Thread.currentThread().getName()+"set "+data3+"number data");
condition2.signal();//线程通知
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void B() {
lock.lock();
try {
//while防止虚假唤醒
while(data3!=2){
condition2.await(); //线程等待
}
data3 = 3;
System.out.println(Thread.currentThread().getName()+"get "+data3+"number data");
condition3.signal();//线程唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void C() {
lock.lock();
try {
//while防止虚假唤醒
while(data3!=3){
condition3.await(); //线程等待
}
data3 = 1;
System.out.println(Thread.currentThread().getName()+"get "+data3+"number data");
condition1.signal();//线程唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}