Java多线程:线程通信:管程法和信号灯法,线程池的使用

线程协作

线程通信

  • 应用场景 : 生产者和消费者问题
  1. 假设仓库中只能存放一件产品 , 生产者将生产出来的产品放入仓库 , 消费者将仓库中产品取走消费 .
  2. 如果仓库中没有产品 , 则生产者将产品放入仓库 , 否则停止生产并等待 , 直到仓库中的产品被消费者取走为止 .
  3. 如果仓库中放有产品 , 则消费者可以将产品取走消费 , 否则停止消费并等待 ,直到仓库中再次放入产品为止 .
    Java多线程:线程通信:管程法和信号灯法,线程池的使用_第1张图片

线程通信-分析

  • 这是一个线程同步问题 , 生产者和消费者共享同一个资源 , 并且生产者和消费者之间相互依赖 , 互为条件 .
  1. 对于生产者 , 没有生产产品之前 , 要通知消费者等待 . 而生产了产品之后 , 又需要马上通知消费者消费
  2. 对于消费者 , 在消费之后 , 要通知生产者已经结束消费 , 需要生产新的产品以供消费.
  3. 在生产者消费者问题中 , 仅有synchronized是不够的
    synchronized 可阻止并发更新同一个共享资源 , 实现了同步
    synchronized 不能用来实现不同线程之间的消息传递 (通信)

Java提供了几个方法解决线程之间的通信问题

Java多线程:线程通信:管程法和信号灯法,线程池的使用_第2张图片
注意 : 均是Object类的方法 , 都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException

方式一

  • 并发协作模型 “ 生产者 / 消费者模式 ” —>管程法
  1. 生产者 : 负责生产数据的模块 (可能是方法 , 对象 , 线程 , 进程) ;
  2. 消费者 : 负责处理数据的模块 (可能是方法 , 对象 , 线程 , 进程) ;
  3. 缓冲区 : 消费者不能直接使用生产者的数据 , 他们之间有个 “ 缓冲区生产者将生产好的数据放入缓冲区 , 消费者从缓冲区拿出数据
    Java多线程:线程通信:管程法和信号灯法,线程池的使用_第3张图片
    案例:
//思路

//1.思考需要哪些对象?
// 生产 , 消费 , 产品 , 容器

//2.分工

/*
    生产者只管生产
    消费者只管消费
    鸡: 实体类

    容器 :

    容器添加数据.
    要判断容器是否满 , 满了等待消费者消费
    没有满,通知生产者生产

    容器减少数据
    判断还有没有数据, 没有数据的话 . 等待生产者生产
    消费完毕 , 通知生产者生产
 */


import java.sql.SQLOutput;

//测试生产者和消费者问题
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}


//生产者
class Productor extends Thread{
    //需要向容器中加入产品
    SynContainer container;
    public Productor(SynContainer container){
        this.container = container;
        }
        @Override
        public void run() {
            for (int i = 1; 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 = 1; i < 100; i++) {
            //消费者拿走产品
            Chicken chicken = container.pop();
            System.out.println("消费者消费了"+chicken.id+"鸡");
        }
    }
}


//缓冲区-->容器
class SynContainer{

    //容器
    Chicken[] chickens = new Chicken[10];



    //容器的计数器
    int num = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {

        //假如容易已经满了,就不用放,等待消费者消费
        if (num>=chickens.length){

            //等待消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //假如容器没有满 , 通知生产生成

        System.out.println("num,,,,,"+num);
        chickens[num] = chicken;
        System.out.println("数组有多少个元素"+num);
        num++;
        //通知消费者消费
        this.notifyAll();

    }

    //消费者拿走产品
    public synchronized Chicken pop(){
        //假如容器空的,等待
        if (num<=0){
            //等待生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        num--;
        Chicken chicken = chickens[num];
        //通知生产者生产
        this.notifyAll();
        return chicken;
    }


}

//产品->鸡
class Chicken {
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

方式二

  • 并发协作模型 “ 生产者 / 消费者模式 ” —>信号灯法

案例:

//生产者消费2
//生产者--->演员
//消费者--->观众
//产品:信号灯--->电视----->声音

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();

        new Player(tv).start();
        new Watcher(tv).start();
    }
}


//生产者
class Player extends Thread{
    TV tv;

    public Player(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("节目:快乐大本营播放中");
                System.out.println();
            }else {
                this.tv.play("广告:抖音,记录美好生活");
            }
        }
    }
}

//消费者
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//电视
class TV{
    //演员说话 , 观众等待
    //观众观看 , 演员等待
    boolean flag = true;

    //说话
    String voice;

    //表演
    public synchronized void play(String voice){

        //演员等待
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("表演了"+voice);
        this.voice = voice;

        //让观众观看
        this.notifyAll();
        this.flag = !this.flag;

    }


    //观看
    public synchronized void watch(){


        //观众等待
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("观众听到了: "+voice);

        //通知演员说话
        this.notifyAll();

        this.flag = !this.flag;
    }

}

线程池的使用

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影
    响很大。
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
  • 好处:
  1. 提高响应速度(减少了创建新线程的时间)
  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  3. 便于线程管理(…)
  • corePoolSize:核心池的大小

  • maximumPoolSize:最大线程数

  • keepAliveTime:线程没有任务时最多保持多长时间后会终止

  • JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors

  1. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
    Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
    void shutdown() :关闭连接池
  2. Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

案例一:

//线程创建方式
/*
1.继承Thread
2.实现Runnable接口
3.实现Callable接口
4.使用线程池
 */


import java.util.concurrent.*;

class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread1");
    }
}


class MyThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("MyThread2");
    }
}

class MyThread3 implements Callable{

    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 200;
    }
}


public class ThreadNew {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new MyThread1().start();
        new Thread(new MyThread2()).start();


        FutureTask future = new FutureTask(new MyThread3());
        new Thread(future).start();


        Integer integer = future.get();
        System.out.println(integer);

    }


}

结果输出:
MyThread1
MyThread2
MyThread3
200

案例二:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//使用线程池
public class ThreadPool{

    public static void main(String[] args) {

        //创建一个线程池(池子大小)
        ExecutorService pool = Executors.newFixedThreadPool(10);

        //执行runnable接口实现类
        pool.execute(new MyThread4());
        pool.execute(new MyThread4());
        pool.execute(new MyThread4());
        pool.execute(new MyThread4());

        //关闭连接池
        pool.shutdown();

    }

}

class MyThread4 implements Runnable{
    @Override
    public void run() {
        System.out.println("MyThread4");
    }
}

你可能感兴趣的:(Java学习)