在高并发的环境下,出现了一个生产者消费者问题,如何保证生产者和消费者能够保持顺序执行,不会出现资源争抢问题?
有两种解决方案:
我们先假设有如下场景:
有一个数字0,有两个方法,第一个方法是检测数字是否为0,如果为0则启动等待线程,并为数字加一,然后通知所有其他线程。第二个方法则是当数字不为0时,执行数字减一操作。
第一种:利用 传统方法先给业务代码的方法加一个同步锁,然后有序的等待和通知,即加一个wait和notifyAll方法。
代码如下:
public class Day2 {
public static void main(String[] args) {
C c=new C();
new Thread(()->{
for(int i=0;i<10;i++){
try {
c.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++){
try {
c.decrease();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for(int i=0;i<10;i++){
try {
c.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for(int i=0;i<10;i++){
try {
c.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class C {
int num=0;
public synchronized void add() throws InterruptedException {
while (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+":"+num);
this.notifyAll();
}
public synchronized void decrease() throws InterruptedException {
while (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+":"+num);
this.notifyAll();
}
}
第二个方法是利用Lock方法:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Day2 {
public static void main(String[] args) {
C c=new C();
new Thread(()->{
for(int i=0;i<10;i++){
c.add();
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++){
c.decrease();
}
},"B").start();
new Thread(()->{
for(int i=0;i<10;i++){
c.add();
}
},"C").start();
new Thread(()->{
for(int i=0;i<10;i++){
c.add();
}
},"D").start();
}
}
class C {
int num=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public void add(){
lock.lock();
try {
while (num!=0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+":"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrease() {
lock.lock();
try {
while (num==0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+":"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
这两种方法其实都一样实现了锁的交替执行,而且方法一样,那么为什么现在更多时候是用第二种方法,它的优点是什么?首先当我们执行上面的两种方法时会发现,当我们启动的只有两个线程时,没问题,但是三个或者四个或者更多的时候,你会发现三个线程的执行顺序是不唯一的,但是我们想要它们按照顺序执行的时候怎么办?这才是为什么Lock锁出现的原因,它相比synchronized有了一些新的优点,那就是它能够实现精准通知唤醒,我们只需要为每一个方法添加一个同步监视器condition就可以了。
我们把第二个方法修改如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Day2 {
public static void main(String[] args) {
C c=new C();
new Thread(()->{
for(int i=0;i<10;i++){
c.add();
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++){
c.decrease();
}
},"B").start();
new Thread(()->{
for(int i=0;i<10;i++){
c.add1();
}
},"C").start();
new Thread(()->{
for(int i=0;i<10;i++){
c.decrease1();
}
},"D").start();
}
}
class C {
int num=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
Condition condition1=lock.newCondition();
Condition condition2=lock.newCondition();
Condition condition3=lock.newCondition();
public void add(){
lock.lock();
try {
while (num!=0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+":"+num);
condition1.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrease() {
lock.lock();
try {
while (num==0){
condition1.await();
}
num--;
System.out.println(Thread.currentThread().getName()+":"+num);
condition2.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void add1(){
lock.lock();
try {
while (num!=0){
condition2.await();
}
num++;
System.out.println(Thread.currentThread().getName()+":"+num);
condition3.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrease1() {
lock.lock();
try {
while (num==0){
condition3.await();
}
num--;
System.out.println(Thread.currentThread().getName()+":"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}