Java多线程:线程协作、线程池

线程协作

线程通信

应用场景:生产者和消费者问题,生产者生产商品,消费者购买商品,没有商品时等待生产商生产放入商品

分析

线程同步问题。生产者和消费者共享同一资源,相互依赖,互为条件:

  • 生产者没有生产产品前,需要通知消费者等待,生产后要通知消费者消费
  • 消费者在消费后要通知生产者结束消费,生产新的产品以供消费
  • synchronized不足使用:
    • synchronized可以同步
    • 但无法通信

方法

Java提供了一些Object类的方法解决线程间的通信问题,仅能在同步方法和同步块中使用,否则会抛出异常

  • wait():线程会一直等待,直到其他线程通知,会释放锁
  • wait(long timeout):指定等待的毫秒数
  • notify():唤醒一个处于等待状态的线
  • notifyAll(): 唤醒同一个对象上所有地道用wait()方法的线程,优先级别高的线程优先调度

管程法:利用缓冲区

涉及wait()时使用while

//管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        //测试,多名生产者和多名消费者
        new Producer(container).start();
        new Producer(container).start();
        new Producer(container).start();
        new Consumer(container).start();
        new Consumer(container).start();
        new Consumer(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 Food(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++) {
            System.out.println("消费了:" + container.pop().id);
        }
    }
}

//产品
class Food{
    int id;

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

//缓冲区
class SynContainer{
    //容器大小
    Food[] foods = new Food[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Food food){
        //容器满了,等待消费者消费
        while (count == foods.length){
            //通知消费者消费,生产等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //没满,生产产品
        foods[count] = food;
        count++;

        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Food pop(){
        //判断是否有产品,多名消费者都等待
        while (count <= 0){
            //等待产品生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //可以消费
        count--;//出栈,因为生产后索引指向后一个空位
        Food food = foods[count];

        //消耗了,通知生产者生产
        this.notifyAll();
        return food;
    }
}

信号灯法:标志位

//信号灯法,标志位解决
public class TestPC2 {
    public static void main(String[] args) {
        Produce produce = new Produce();
        new Producer2(produce).start();
        new Producer2(produce).start();
        new Consumer2(produce).start();
        new Consumer2(produce).start();
    }
}
//生产者
class Producer2 extends Thread{
    Produce id;

    public Producer2(Produce id) {
        this.id = id;
    }

    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            this.id.product(i);
        }
    }
}

//消费者
class Consumer2 extends Thread{
    Produce id;

    public Consumer2(Produce id) {
        this.id = id;
    }

    //消费
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            this.id.consume();
        }
    }
}

class Produce{
    //生产者生产,消费者等待 T
    //消费者消费,生产者等待 F
    int id;
    boolean flag = true;

    //生产
    public synchronized void product(int id){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生产了" + id);
        //通知消费者消费
        this.notifyAll();
        this.id = id;
        this.flag = !this.flag;
    }

    //消费
    public synchronized void consume(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费了" + id);

        //通知生产
        this.notifyAll();
        this.flag = !this.flag;
    }
}

线程池

  • 背景:经常创建和销毁、使用量特别 大的资源,如并发情况下的线程,对性能影响很大
  • 思路:提前创建好多个线程,放入线程池中,使用时之间获取,使用完放回池中。避免频发创建销毁,实现重复利用
  • 优点:
    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中线程,不需要每次重新创建)
    • 便于线程管理:
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没用任务时最多保留多久中止

线程池的使用

  • JDK5.0,ExecutorServiceExecutors
  • ExecutorService:线程池接口,常用子类ThreadPoolExecutor
    • void execute(Runnable command):执行任务/命令,没返回值,用于执行Runnable
    • <T>Future<T> submit(Callable<T> task):执行任务,有返回值,用于执行Callable
    • void shutdown():用于关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
public class TestPool {
    public static void main(String[] args) {
        //创建服务,创建线程池
        //newFixedThreadPool,参数为线程大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //关闭
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2022-04-13 14:57  chachan53  阅读(47)  评论(0编辑  收藏  举报