Ⅰ.模拟一个工厂类
属性:库存;(用来确定是否生产或者消费)
方法:生产(…){
…… ……
}
消费(…){
…… ……
}
生产方法:当库存不足时,就需要自加(模拟生产),当库存足够时,就暂停生产。
消费方法:当库存足够时,就需要自减(模拟消费),当库存不足时,就暂停消费。
public class Factory {
// 库存
int count;
// 生产功能
public synchronized void production() {
// 当库存大于等于1时,就暂停生产
while (count >= 1) {
try {
this.wait();// 线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 当库存小于1时,就开始生产
count++;
System.out.println(Thread.currentThread().getName() + "生产了一个商品,当前库存剩余:" + count);
this.notifyAll();// 生产一个商品之后,就可以唤醒消费了
}
// 消费功能
public synchronized void consumption() {
// 当库存小于等于0时,就暂停消费
while (count <= 0) {
try {
this.wait();// 线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 当库存大于0时,就开始消费
count--;
System.out.println(Thread.currentThread().getName() + "消费了一个商品,当前库存剩余:" + count);
this.notifyAll();// 消费一个商品之后,就可以唤醒生产了
}
}
Ⅱ.模拟两个线程任务类(生产任务,消费任务)
分别继承Runnable接口。线程任务类内部,都需要实现带参构造方法,将工厂对象作为形参,而不能在其内部直接new出工厂类对象。
原因:我们需要保证操作的是同一工厂对象,所以最好的解决办法就是在测试类中new出工厂类对象,然后在new出线程任务类对象的时候,把工厂类对象作为实参进行传递。
①生产线程任务类
//生产任务类
public class Production implements Runnable {
Factory plant;
public Production(Factory plant) {
this.plant = plant;
}
@Override
// 调用工厂的生产功能
public void run() {
//循环生产
while (true) {
plant.production();
//生产一件商品之后,就休息1000ms
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
②消费线程任务类
//消费任务类
public class Consumption implements Runnable {
Factory plant;
public Consumption(Factory plant) {
this.plant = plant;
}
@Override
// 调用工厂的消费功能
public void run() {
//循环消费
while (true) {
plant.consumption();
//消费一件商品之后,就休息1000ms
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Ⅲ.模拟测试类
①new出工厂类对象
②new出两个线程任务类对象(生产、消费),并将工厂类对象作为实参进行传递
③new出线程对象,并将线程任务类对象进行传递
④分别启动线程
public class Test {
public static void main(String[] args) {
// 工厂对象
Factory plant = new Factory();
// 线程任务对象
// 生产对象
Production proTask = new Production(plant);
// 消费对象
Consumption conTask = new Consumption(plant);
// 线程对象
// 生产对象
Thread t0 = new Thread(proTask);
Thread t1 = new Thread(proTask);
// 消费对象
Thread t2 = new Thread(conTask);
Thread t3 = new Thread(conTask);
// 启动线程
t0.start();
t1.start();
t2.start();
t3.start();
}
}
注:为避免多个线程同时操作一个线程任务而出错,所以我们需要实现同步
同步方法:①同步代码:工厂类内的生产和消费方法,通过synchronized( 锁对象 ){ …… }同步
②同步函数:通过synchronized修饰,直接将工厂类内的生产和消费方法同步
③Lock锁对象:创建单独的Lock锁对象,通过lock.lock();和lock.unlock();进行同步
运行后效果图
解释
① this.wait():线程等待,锁对象不会继续占用,立即释放锁对象
this.notifyAll():线程唤醒,唤醒时不会立即执行,仍会等到锁对象的使用
Thread.sleep(long millis):线程睡眠,锁对象将继续占用,不会释放
注: this.wait();和this.notifyAll();需配合使用,且只能用于同步中,否则会抛出异常
Thread.sleep(long millis);需传入实参(时间,单位:ms)
② Thread.currentThread().getName():获取当前进入的线程名