前言
最近也看完了<<并发编程的艺术>>,也零零散散的看了不少多线程有关的东西。早上写代码的时候看到一篇博客讲了使用notify和wait相关的一些东西。本人自己对多线程一直有点苦手,书本看了一大堆。但是实际使用依然头大。这次突然对于多线程编码有点感悟,记录于此。
概念相关
synchronized{}锁住的代码块,结束代码执行之后。会释放对应的锁
wait和notify,可以在synchronized{}之中使用。即使拿到了锁,也会在代码块中释放出去。
题目1
现在有3个生产者,3个消费者,生产者每次+1。消费者每次-1。库存从0开始,不能小于0。也不能大于5。要线程安全的进行生产和消费。
解答:多线程的编程处理的核心问题就在于变量。
一般有如下2个原则:
1、共享变量处理,使用static共享,或者传入同一个类控制
2、每个线程控制自己的私有变量,ThreadLocal中的实现
代码实现
这是一个库存类
import java.util.ArrayList;
import java.util.List;
public class Storage {
private List<Object> foods;
public final static int MAX_SIZE = 5;
private boolean flag = false;
public Storage(){
foods = new ArrayList<Object>();
}
public List<Object> getFoods() {
return foods;
}
public void setFoods(List<Object> foods) {
this.foods = foods;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
线程类
package main.java;
public class Company3 {
public static void main(String[] args) {
Company3 c = new Company3();
Storage storage = new Storage();
new Thread(c.new Customer(storage)).start();
new Thread(c.new Producer(storage)).start();
new Thread(c.new Customer(storage)).start();
new Thread(c.new Producer(storage)).start();
new Thread(c.new Customer(storage)).start();
new Thread(c.new Producer(storage)).start();
}
/**
* 消费者
*/
private class Customer implements Runnable {
private Storage storage;
public Customer(Storage storage) {
super();
this.storage = storage;
}
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (storage) {
System.out.println("消费者" + Thread.currentThread().getName() + "获得锁");
while (storage.getFoods().size() <= 0) {
System.out.println("货物已空,提示生产者生产");
try {
System.out.println("消费者" + Thread.currentThread().getName() + "开始等待");
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.getFoods().remove(0);
System.out.println("消费者消费1, " + Thread.currentThread().getName() + ", 余量:" + storage.getFoods().size());
storage.notifyAll();
}
}
}
}
/**
* 生产者
*/
private class Producer implements Runnable {
private Storage storage;
public Producer(Storage storage) {
super();
this.storage = storage;
}
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (storage) {
System.out.println("生产者" + Thread.currentThread().getName() + "获得锁");
while (storage.getFoods().size() >= Storage.MAX_SIZE) {
System.out.println("货物已满,提示消费者消费");
try {
System.out.println("生产者" + Thread.currentThread().getName() + "开始等待");
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.getFoods().add(1);
System.out.println("生产者生产1, " + Thread.currentThread().getName() + ",余量:" + storage.getFoods().size());
storage.notifyAll();
}
}
}
}
}
效果如下
消费者消费1, Thread-0, 余量:2
消费者Thread-2获得锁
消费者消费1, Thread-2, 余量:1
消费者Thread-4获得锁
消费者消费1, Thread-4, 余量:0
生产者Thread-3获得锁
生产者生产1, Thread-3,余量:1
生产者Thread-1获得锁
生产者生产1, Thread-1,余量:2
生产者Thread-5获得锁
生产者生产1, Thread-5,余量:3
消费者Thread-0获得锁
消费者消费1, Thread-0, 余量:2
消费者Thread-2获得锁
消费者消费1, Thread-2, 余量:1
消费者Thread-4获得锁
消费者消费1, Thread-4, 余量:0
消费者Thread-0获得锁
货物已空,提示生产者生产
讲解。
一个生产者类,一个消费者类。通过共享storage获得一个显式的变量进行控制,每次只能允许一个线程获得storage的锁。对其中的参数进行操作。当库存为0时,消费者wait停下当前线程,并且释放锁。循环等待库存增多,其他线程进行争抢锁进行消费,同时唤醒其他线程,同理对于库存5时,生产者也是一样
tips:
1、我的线程在启动后进行循环的头位置,加了一个sleep。是因为不加sleep的话,会导致偏向锁很严重,一直会是该线程持有锁,所以用sleep让其他线程也可以争抢到锁。展示效果更佳明显
2、如果添加更多的消费者或者生产者,那么如果是更多消费者,会看到当消费到库存为0的时候,会有更多的消费者线程争抢到线程,但是获得不到锁,就会wait等待中
题目2
我希望修改消费者和生产者的模式,当库存为0时,所有消费者停止消费,等待生产者生产,当生产者生产到5时,所有生产者也会停止,等待消费者消费
代码2
public class Company {
public static void main(String[] args) {
Company c = new Company();
Storage storage = new Storage();
new Thread(c.new Customer(storage)).start();
new Thread(c.new Producer(storage)).start();
new Thread(c.new Customer(storage)).start();
new Thread(c.new Producer(storage)).start();
}
/**
* 消费者
*
* @author xtuali
*/
private class Customer implements Runnable {
private Storage storage;
public Customer(Storage storage) {
super();
this.storage = storage;
}
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (storage) {
System.out.println("消费者" + Thread.currentThread().getName() + "获得锁" );
if (storage.isFlag()) {
while (storage.getFoods().size() <= 0) {
System.out.println("货物已空,提示生产者生产");
storage.setFlag(false);
try {
storage.notifyAll();
System.out.println("消费者" + Thread.currentThread().getName() + "开始等待" );
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.getFoods().remove(0);
System.out.println("消费者消费1, " + Thread.currentThread().getName() + ", 余量:" + storage.getFoods().size());
}else{
try {
System.out.println("消费者" + Thread.currentThread().getName() + "开始等待" );
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 生产者
*
* @author xtuali
*/
private class Producer implements Runnable {
private Storage storage;
public Producer(Storage storage) {
super();
this.storage = storage;
}
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (storage) {
System.out.println("生产者" + Thread.currentThread().getName() + "获得锁" );
if(!storage.isFlag()){
while (storage.getFoods().size() >= Storage.MAX_SIZE) {
System.out.println("货物已满,提示消费者消费");
storage.setFlag(true);
try {
storage.notifyAll();
System.out.println("生产者" + Thread.currentThread().getName() + "开始等待" );
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.getFoods().add(1);
System.out.println("生产者生产1, " + Thread.currentThread().getName() + ",余量:" + storage.getFoods().size());
}else{
try {
System.out.println("生产者" + Thread.currentThread().getName() + "开始等待" );
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
结果
消费者Thread-0获得锁
消费者Thread-0开始等待
生产者Thread-1获得锁
生产者生产1, Thread-1,余量:1
消费者Thread-2获得锁
消费者Thread-2开始等待
生产者Thread-3获得锁
生产者生产1, Thread-3,余量:2
生产者Thread-1获得锁
生产者生产1, Thread-1,余量:3
生产者Thread-3获得锁
生产者生产1, Thread-3,余量:4
生产者Thread-1获得锁
生产者生产1, Thread-1,余量:5
生产者Thread-3获得锁
货物已满,提示消费者消费
生产者Thread-3开始等待
消费者Thread-0获得锁
消费者消费1, Thread-0, 余量:4
消费者Thread-2获得锁
消费者消费1, Thread-2, 余量:3
解答:
我在Storage类中已经添加了flag标记,flag初始标记设置为false,因为开始的时候没有库存,一切都要等待生产者生产。flag也用于控制消费者在生产者生产的时候放弃消费并等待,也用于控制生产者在消费者消费的时候放弃生产,等待消费。