方法一:信号量
这是我们学习操作系统的时候使用的方法,而且思路也一样。
import java.util.concurrent.Semaphore;
/**
* 使用semaphore信号量实现
*/
public class Test1 {
private static Integer count = 0; // 苹果数量
//创建三个信号量
final Semaphore notFull = new Semaphore(10); //盘子里最多放10个苹果
final Semaphore notEmpty = new Semaphore(0); // 盘子为空
final Semaphore mutex = new Semaphore(1); // 互斥信号量
public static void main(String[] args) {
Test1 test1 = new Test1();
new Thread(test4.new Producer()).start();
new Thread(test4.new Consumer()).start();
new Thread(test4.new Producer()).start();
new Thread(test4.new Consumer()).start();
new Thread(test4.new Producer()).start();
new Thread(test4.new Consumer()).start();
new Thread(test4.new Producer()).start();
new Thread(test4.new Consumer()).start();
}
// 生产者生产苹果
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
notFull.acquire(); //盘子容量减一
mutex.acquire();
count++; // 每次生产一个苹果
System.out.println(Thread.currentThread().getName()
+ "生产者生产,目前总共有" + count);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mutex.release();
notEmpty.release(); // 盘子里的苹果数量加1
}
}
}
}
//消费者消费苹果
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
notEmpty.acquire(); // 盘子里苹果数量减一
mutex.acquire();
count--; // 每次消费一个苹果
System.out.println(Thread.currentThread().getName()
+ "消费者消费,目前总共有" + count);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mutex.release();
notFull.release(); // 盘子多出一个空间
}
}
}
}
}
方法二:使用synchronized实现(等待唤醒机制)
这也是最简单最基础的实现,缓冲区满和为空时都调用wait()方法等待,当生产者生产了一个产品或者消费者消费了一个产品之后会唤醒所有线程。
/**
* 生产者和消费者,wait()和notify()的实现
*/
public class Test1 {
private static Integer count = 0;
private static final Integer FULL = 10;
private static String LOCK = "lock";
public static void main(String[] args) {
Test1 test1 = new Test1();
new Thread(test1.new Producer()).start();
new Thread(test1.new Consumer()).start();
new Thread(test1.new Producer()).start();
new Thread(test1.new Consumer()).start();
new Thread(test1.new Producer()).start();
new Thread(test1.new Consumer()).start();
new Thread(test1.new Producer()).start();
new Thread(test1.new Consumer()).start();
}
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (LOCK) {
while (count == FULL) {
try {
LOCK.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
count++;
System.out.println(Thread.currentThread().getName() + "生产者生产,目前总共有" + count);
LOCK.notifyAll();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LOCK) {
while (count == 0) {
try {
LOCK.wait();
} catch (Exception e) {
}
}
count--;
System.out.println(Thread.currentThread().getName() + "消费者消费,目前总共有" + count);
LOCK.notifyAll();
}
}
}
}
}
方法三:可重入锁ReentrantLock的实现
这里我们使用的是 Java 提供的 ReentrantLock 来实现生产者/消费者模型,与 synchronized 相比之下,一个 lock 我们可以生成多个 condition ,换句话说 synchronized 就像是只有一个 condition 的 ReentrantLock ,所以 后者比前者更加的灵活,但是也较为麻烦,因为每次都得手动地关闭锁,所以我们每次得尝试在 finally 里面关闭锁。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 生产者和消费者,ReentrantLock的实现
*/
public class Test2 {
private static Integer count = 0;
private static final Integer FULL = 10;
//创建一个锁对象
private Lock lock = new ReentrantLock();
//创建两个条件变量,一个为缓冲区非满,一个为缓冲区非空
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
Test2 test2 = new Test2();
new Thread(test2.new Producer()).start();
new Thread(test2.new Consumer()).start();
new Thread(test2.new Producer()).start();
new Thread(test2.new Consumer()).start();
new Thread(test2.new Producer()).start();
new Thread(test2.new Consumer()).start();
new Thread(test2.new Producer()).start();
new Thread(test2.new Consumer()).start();
}
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
//获取锁
lock.lock();
try {
while (count == FULL) {
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println(Thread.currentThread().getName()
+ "生产者生产,目前总共有" + count);
//唤醒消费者
notEmpty.signal();
} finally {
//释放锁
lock.unlock();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
lock.lock();
try {
while (count == 0) {
try {
notEmpty.await();
} catch (Exception e) {
e.printStackTrace();
}
}
count--;
System.out.println(Thread.currentThread().getName()
+ "消费者消费,目前总共有" + count);
notFull.signal();
} finally {
lock.unlock();
}
}
}
}
}
方法四:阻塞队列BlockingQueue的实现
阻塞队列可以借助它本身的性质帮我们实现生产者/消费者模型,在某些情况下,访问队列会造成阻塞,队列被阻塞分为两种情况:
当队列满了的时候进行入队列操作
当队列空了的时候进行出队列操作
因此,当一个线程对已经满了的阻塞队列进行入队操作时会阻塞,除非有另外一个线程进行了出队操作,当一个线程对一个空的阻塞队列进行出队操作时也会阻塞,除非有另外一个线程进行了入队操作。所以阻塞队列本身就可以完成生产者/消费者模型。
public class Demo2 {
private int count = 0;
private BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
for (int i = 1; i <= 5; i++) {
new Thread(demo2.new Producer(), "生产者-" + i).start();
new Thread(demo2.new Consumer(), "消费者-" + i).start();
}
}
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
try {
queue.put(1);
count++;
System.out.println("生产者 " + Thread.currentThread().getName() + " 总共有 " + count + " 个资源");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
queue.take();
count--;
System.out.println("消费者 " + Thread.currentThread().getName() + " 总共有 " + count + " 个资源");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}