线程协作

生产者消费者模式

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.

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

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

方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAII() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

管程法

生产者消费者模型利用缓冲区解决

public class TestPC {
    public static void main(String[] args) {
        SynContainer s =  new SynContainer();
        new Producer(s).start();
        new Consumer(s).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.addChicken(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++) {
            System.out.println("消费了"+container.removeChicken().id+"只鸡");
        }
    }
}

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

class SynContainer {
    // 制定容器大小
    Chicken[] chickens = new Chicken[10];
    // 计数器
    int count = 0;
    // 生产者放入产品
    public synchronized void addChicken(Chicken chicken)  {
        while (count == chickens.length) {
            // 通知消费者消费,生产等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        chickens[count++] = chicken;
        // 通知消费
        this.notifyAll();
    }
    public synchronized Chicken removeChicken() {
        while (count == 0) {
            // 等待生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        Chicken chicken = chickens[--count];
        // 通知生产
        this.notifyAll();
        return chicken;
    }

}

在wait()的使用场景中,必须用while循环包裹条件判断,而非if。这是多线程编程的经典规范,目的是:
应对虚假唤醒,确保线程不会在条件不满足时错误执行;
处理多线程竞争下,唤醒后条件已失效的情况,保证逻辑正确性。

信号灯法

// 信号灯法,标志位解决
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Actor(tv).start();
        new Audience(tv).start();
    }
}

// 生产者 -> 演员
class Actor extends Thread {
    TV tv;
    Actor(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                this.tv.play("java...");
            } else {
                this.tv.play("golang... ");
            }
        }
    }

}
// 消费者 -> 观众
class Audience extends Thread {
    TV tv;
    Audience(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.watch();
        }
    }
}
// 产品 -> 节目
class TV {
    // 拍电影 观众等, 看电影 演员等
    String voice;
    boolean flag = true;    // true -> 没有电影
    // 表演
    public synchronized void play(String voice) {
        while (!flag) {  // 注意:这里是!flag,因为flag=false表示已有节目,需等待消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("playing " + voice);
        // 通知观看
        this.voice = voice;
        this.flag = false;
        this.notifyAll();   // 锁在方法或代码块执行执行完后才释放,所以通知也可以放在前面
    }
    // 观看
    public synchronized void watch() {
        while (this.flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        // 通知表演
        System.out.println("watching " + this.voice);
        this.flag = true;
        this.notifyAll();
    }
}

线程池

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

public class TestPool {
    public static void main(String[] args) {
        // 创建线程池,制定参数大小
        ExecutorService pool = Executors.newFixedThreadPool(10);
        // 执行
        pool.execute(new myThread());
        pool.execute(new myThread());
        pool.execute(new myThread());
        pool.execute(new myThread());
        pool.execute(new myThread());
        // 关闭
        pool.shutdown();
    }
}

class myThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2025-11-03 15:45  Huaixuxm  阅读(4)  评论(0)    收藏  举报