示例:多窗口售票。
- 第一步:定义类表示车票池类:
public class TicketPool {
private int ticketAmount =20; //值表示一共有多少张票,票号
public synchronized int getTicket(){ //锁的是当前对象,即票池
int temp = ticketAmount;
if(ticketAmount == 0){
return 0; //没有票了
}
ticketAmount -- ; //每卖出一张总票数减1
return temp; //返回当前售出的票号
}
}
- 第二步:定义售票员类:
public class Saler extends Thread {
private String name;
private TicketPool pool;
public Saler(String name,TicketPool pool) {
super();
this.name = name;
this.pool = pool;
}
@Override
public void run() {//卖票
while (true){
int ticketNum = pool.getTicket();//从票池中取票
if(ticketNum ==0){
return;
}
System.out.println(name+":"+ticketNum);
}
}
}
- 测试代码:
public class TicketTest {
public static void main(String[] args) {
TicketPool pool = new TicketPool();
Saler zhangsan = new Saler("张三",pool);
Saler tom = new Saler("tom",pool);
zhangsan.start();
tom.start();
}
}
生产消费
演示由于生产的快消费的慢导致爆仓的情况
- 产品池
public class ProductPool {
private LinkedList pool = new LinkedList();//存放产品
//同一时刻只能有一个生产者
public synchronized int add(Integer item) { //返回当前生产的产品
pool.add(item); //增加产品
return item;
}
//同一时刻只能有一个消费者
public synchronized int remove() { //返回当前消费的产品
if (pool.isEmpty()) { //如果产品池为空的话,返回-1
return -1;
}
return pool.removeFirst();
}
}
- 生产者
public class Producer extends Thread {
private static int index = 0; //产品标识
private String name;
private ProductPool pool;
public Producer(String name, ProductPool pool) {
this.name = name;
this.pool = pool;
}
@Override
public void run() {
while (true) { //一直不停地生产
int res = pool.add(index++); //将产品放到产品池中
System.out.println(name+"+"+res);
}
}
}
- 消费者
public class Consumer extends Thread {
private String name;
private ProductPool pool;
public Consumer(String name, ProductPool pool) {
this.name = name;
this.pool = pool;
}
@Override
public void run() { //一直不停地消费
while (true) {
int res = pool.remove(); //从产品池中移除产品
System.out.println(name+"-"+res);
}
}
}
- 测试代码
public static void main(String[] args) {
ProductPool pool = new ProductPool();
Producer p1 = new Producer("p1", pool);
Producer p2 = new Producer("p2", pool);
Consumer c1 = new Consumer("c", pool);
p1.start();
p2.start();
c1.start();
}
- 运行程序 为了尽快看到程序出错效果(内存溢出),配置运行选项:
结果:
解决办法:指定一个变量,标记产品池中最多能放多少产品。当产品达到这个阀值时,不再生产,当低于这个值时再生产。
- 产品池
public class ProductPool {
private int threshold = 100; //创建中产品数量最大值
private LinkedList pool = new LinkedList();//放产品
//同一时刻只能有一个人生产
//返回增加后线程池中产品的数量
public synchronized int add(Integer item) {
//如果大于threshold,进入等待队列,释放锁控制权,别的线程拿到控制权后做自己具体的业务
//有可能出现,当前线程在重新获取锁的控制权,开始往产品池中添加产品时,发现池子满的情况,所以此处不采用if而使用while
//如果再次获取锁控制权,就继续执行上次wait后面的代码,wait之后首先再次执行while循环,再次判断,如果还是满的,就再等……直到仓库不满时才能添加
// 当线程被唤醒后,它就立即获取锁控制权,从之前等待的位置继续运行
// 拓展:wait一般和while循环结合使用
while (pool.size() >= threshold) {
try {
this.wait(); //将当前线程放到等待队列中(谁在调用这个方法,谁就是当前线程)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.add(item); //增加产品,当获取到通知被唤醒时,线程获取锁的控制权,执行这行代码
// System.out.println("++++"+pool.size()); //生产之后产品的数量
notify();//增加完商品之后,通知别的线程可以生产or消费了
return item;
}
// 同一时刻只能有一个人消费
public synchronized int remove() {//返回当前取到的产品
while (pool.isEmpty()) {
try {
this.wait();//如果仓库中没有产品,线程就等着
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Integer res = pool.removeFirst();
// System.out.println("----"+pool.size()); //消费之后,仓库中产品的数量
notify();//消费完商品之后,通知别的线程可以生产or消费了
return res;
}
}
- 生产者
public class Producer extends Thread {
private static int index = 0;
private String name;
private ProductPool pool;
public Producer(String name, ProductPool pool) {
this.name = name;
this.pool = pool;
}
@Override
public void run() {
while (true) {//在产品池中不断的生产
int res = pool.add(index++);
System.out.println(name+"+ "+res);
}
}
}
- 消费者
public class Consumer extends Thread {
private String name;
private ProductPool pool;
public Consumer(String name, ProductPool pool) {
this.name = name;
this.pool = pool;
}
@Override
public void run() {
while (true) {//在产品池中不断的消费
int res = pool.remove();
System.out.println(name+"-"+res);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 测试代码
public class DemoTest {
public static void main(String[] args) {
ProductPool pool = new ProductPool();
Producer p1 = new Producer("p1", pool);
Producer p2 = new Producer("p2", pool);
Consumer c1 = new Consumer("c1", pool);
Consumer c2 = new Consumer("c2", pool);
Consumer c3 = new Consumer("c3", pool);
p1.start();
p2.start();
c1.start();
c2.start();
c3.start();
}
}
采用等待+通知的方式解决爆仓问题:代码改进
- 产品池
public class ProductPool {
private int threshold = 100;
private LinkedList pool = new LinkedList();//存放产品
//同一时刻只能有一个人生产
public void add(Integer item) {
synchronized (this) {
String name = Thread.currentThread().getName();
while (pool.size() >= threshold) {
try {
System.out.println(name + ".wait()");
this.wait(); //将当前线程放到等待队列中(谁在调用这个方法,谁就是当前线程)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.add(item); //增加产品,当获取到通知被唤醒时,线程获取锁的控制权,执行这行代码
System.out.println(name + "生产后总数量:" + pool.size());
System.out.println(name + "发送了通知");
notify();//增加完商品之后,通知别的线程可以生产or消费了
}
}
// 同一时刻只能有一个人消费
public void remove() {
synchronized (this) {
String name = Thread.currentThread().getName();
while (pool.isEmpty()) {
try {
System.out.println(name + ".wait()");
this.wait();//如果仓库中没有产品,线程就等着
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.removeFirst();
System.out.println(name + "消费后总数量:" + pool.size());
System.out.println(name + "发送了通知");
notify();//消费完商品之后,通知别的线程可以生产or消费了
}
}
}
- 生产者
public class Producer extends Thread {
private static int index = 0;
private ProductPool pool;
public Producer(ProductPool pool) {
this.pool = pool;
}
@Override
public void run() {
while (true) {//在产品池中不断的生产
pool.add(index++);
}
}
}
- 消费者
public class Consumer extends Thread {
private ProductPool pool;
public Consumer(ProductPool pool) {
this.pool = pool;
}
@Override
public void run() {
while (true) {//在产品池中不断的消费
pool.remove();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 测试代码
public class DemoTest {
public static void main(String[] args) {
ProductPool pool = new ProductPool();
Producer p1 = new Producer(pool);
p1.setName("p1");
Producer p2 = new Producer(pool);
p2.setName("p2");
Consumer c1 = new Consumer(pool);
c1.setName("c1");
p1.start();
p2.start();
c1.start();
}
}