Java 并发设计模式(三)

简介: Thread Local Storage 表示线程本地存储模式。大多数并发问题都是由于变量的共享导致的,多个线程同时读写同一变量便会出现原子性,可见性等问题。局部变量是线程安全的,本质上也是由于各个线程各自拥有自己的变量,避免了变量的共享。

五、Worker - Thread 模式


Worker Thread 模式,对应到现实世界,类似工厂中的工人做任务,当有任务的时候,工人取出任务执行。

解决的办法是使用线程池,并且使用一个阻塞队列来存储任务,线程池中的线程从队列中取出任务执行。线程池的使用需要注意几点:

  • 任务队列尽量使用有界队列,避免任务过多造成 OOM。
  • 应该明确指定拒绝策略,可以根据实际情况实现 RejectedExecutionHandler 接口自定义拒绝策略。
  • 应该给线程指定一个有意义的名字,最好和业务相关。
  • 为不同的任务创建不同的线程池,这样能够有效的避免死锁问题。


六、Two - Phase Termination 模式



1. 两阶段终止概念

Two - Phase Termination,即两阶段终止,主要是为解决如何正确的终止一个线程,这里说的是一个线程终止另一个线程,而不是线程终止自己。

Java 中的线程提供了一个 stop() 方法用来终止线程,这不过这个方法会直接将线程杀死,风险太高,并且这个方法已经被标记为废弃,不建议使用了。

两阶段终止,即将线程的结束分为了两个阶段,第一个阶段是一个线程 T1 向另一个线程 T2 发送终止指令,第二个阶段是线程 T2 响应终止指令。

根据 Java 的线程状态,线程如果要进入 TERMINATED 状态则必须先进入 RUNNABLE 状态,而处于 RUNNABLE 状态的线程有可能转换到休眠状态。

Java 的线程提供了 interrupt() 方法,这个方法的作用便是将线程的状态从休眠状态转换到 RUNNABLE 状态。

切换到 RUNNABLE 状态之后,线程有两种方式可以终止,一是执行完 run() 方法,自动进入终止状态;二是设置一个标志,线程如果检测到这个标志,则退出 run() 方法,这就是两阶段终止的响应终止指令。


2. 程序示例

下面是一个简单的使用 interrupt() 方法和中断标志位来终止线程的示例:

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            //检测到中断则退出
            while (!Thread.currentThread().isInterrupted()){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //重新设置中断标志
                    Thread.currentThread().interrupt();
                }
                System.out.println("I am roseduan");
            }
        });
        thread.start();
        thread.interrupt();
    }
}

程序要每隔三秒打印语句,但是线程启动之后就直接调用了 interrupt() 方法,所以线程直接退出了。需要注意的是这里在捕获异常之后,需要重新设置线程的中断状态,因为 JVM 的异常处理会清除线程的中断状态。

在实际的生产中,并不推荐使用这种方式,因为在 Thread 内部可能会调用其他的方法,而其他的方法并不能够保证正确的处理了线程中断,解决的办法便是自定义一个线程的中断标志,如下所示:

public class Test {
    //自定义中断标志
    private volatile boolean isTerminated = false;
    private Thread thread;
    public synchronized void start(){
        thread = new Thread(() -> {
            //检测到中断则退出
            while (!isTerminated) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //重新设置中断状态
                    Thread.currentThread().interrupt();
                }
                System.out.println("I am roseduan");
            }
            isTerminated = false;
        });
        thread.start();
    }
    //线程终止方法
    public synchronized void stop(){
        isTerminated = true;
        thread.interrupt();
    }
}


3. 终止线程池

Java 中并不太会显式的创建和终止一个线程,使用更多的是线程池。

Java 中的线程池提供了两个方法来终止,分别是 shutdown() 和 shutdownNow() ,两个方法的区别如下:

  • shutdown():拒绝新的任务,等待正在执行的和已经在阻塞队列中的任务执行完后,再关闭线程池
  • shutdownNow():直接关闭线程池,拒绝新的任务,并且中断正在执行的任务,已经在阻塞队列中的任务也不会被执行了。


七、Producer - Consumer 模式


这是较为常用的生产者 - 消费者模式,Java 中的线程池就使用了这种模式,线程的使用方是生产者,提供任务,线程池本身是消费者,取出并执行任务。

生产者 - 消费者模式使用了一个任务队列,生产者将任务添加到队列中,消费者从队列中取出任务执行。

这样的设计的目的有三个:

  • 解耦,生产者和消费者之间没有直接的关联,而是通过队列进行通信。
  • 其次可以实现异步,例如生产者可以不用管消费者的行为,直接将任务添加到队列中。消费者也可以不在乎生产者,直接从队列中取任务。
  • 最后,可以平衡生产者和消费者之间的速度差异。

下面是一个简单的生产者 - 消费者程序示例:

public class ProducerConsumerTest {
    private BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100);
    public void produce() {
        queue.add(new Task());
    }
    public void consume() {
        Task task = queue.poll();
        while (task != null){
            task.execute();
            task = queue.poll();
        }
        System.out.println("没有任务了");
    }
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        //生产者线程,创建10个任务
        Thread producer = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                test.produce();
            }
        });
相关文章
|
8天前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
21 1
|
13天前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
1月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
1月前
|
设计模式 存储 算法
Java设计模式-命令模式(16)
Java设计模式-命令模式(16)
|
1月前
|
设计模式 存储 缓存
Java设计模式 - 解释器模式(24)
Java设计模式 - 解释器模式(24)
|
1月前
|
设计模式 安全 Java
Java设计模式-迭代器模式(21)
Java设计模式-迭代器模式(21)
|
1月前
|
设计模式 缓存 监控
Java设计模式-责任链模式(17)
Java设计模式-责任链模式(17)
|
1月前
|
设计模式 运维 算法
Java设计模式-策略模式(15)
Java设计模式-策略模式(15)
|
1月前
|
设计模式 算法 Java
Java设计模式-模板方法模式(14)
Java设计模式-模板方法模式(14)
|
1月前
|
设计模式 存储 安全
Java设计模式-组合模式(13)
Java设计模式-组合模式(13)