线程协作
生产者消费者模式
某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
这就是一个线程同步的问题了,生产者和消费者之间共用同一个资源,并且生产者和消费者之间相互依赖,此时光用synchronized是无法解决这个问题,因为它无法实现线程之间的通信,所以要引入新的方法
sleep不会释放锁,wait会释放锁
解决方法1--管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了" + i + "只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.pop();//先判断有无鸡可以消费
System.out.println("消费了--->" + container.pop().id + "只鸡");//pop方法返回chicken对象,chicken对象里有id属性
}
}
}
//产品
class Chicken{
//产品编号
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//对象数组,数组的每一个元素都是一个对象的引用,此时对象数组作为容器
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,叫消费者消费产品,生产者停止生产
if(count == 10){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果容器没满,叫生产者生产产品
chickens[count] = chicken;
count++;
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if(count == 0){
//等待生产者生产,消费者进行等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
这里注意一定要先--,否则会有空指针或者数组下标越界的问题,好好理一下逻辑,应该是要先--的,因为生产是先++,所以消费一定是先--才能消费到同一个count的数据
感觉怪怪的,不知道哪里有问题
解决方法2--信号灯法
就是设置一个标志位flag
线程池
有了线程池,可以不用频繁的创建和销毁线程,想用哪个线程直接去线程池里取,用完放回去,提高了性能
直接上代码,其实就是多了一种启动线程的方式
public class TestPool {
public static void main(String[] args) {
//1.创建线程池,创建服务
//newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//2.启动线程
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//之前启动线程的方式
new Thread(new MyThread()).start();
//3.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
结果如图
补充:
1.之前启动线程是通过start()方法,现在可以通过 service.execute()来启动