两阶段终止模式和Balking模式

简介: 两阶段终止模式和Balking模式

一、volatile改进两阶段终止模式

1.1 示例

import lombok.extern.slf4j.Slf4j;
/**
 * Created by xiaowei
 * Date 2022/10/29
 * Description volatile实现两阶段终止
 */
@Slf4j(topic = "c.Test09")
public class Test09 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        log.debug("停止监控");
        tpt.stop();
    }
}
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {
    // 监控线程
    private Thread monitorThread;
    // 停止标记
    private volatile boolean stop = false;
    // 启动监控线程
    public void start() {
        monitorThread = new Thread(() -> {
            while (true) {
                //是否被打断
                if(stop){
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1");
        monitorThread.start();
    }
    //停止监控线程
    public void stop(){
        stop = true;
        monitorThread.interrupt();
    }
}

运行结果

09:55:59.124 c.TwoPhaseTermination [t1] - 执行监控记录
09:56:00.131 c.TwoPhaseTermination [t1] - 执行监控记录
09:56:01.132 c.TwoPhaseTermination [t1] - 执行监控记录
09:56:01.623 c.Test09 [main] - 停止监控
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.lilinchao.concurrent.demo_03.TwoPhaseTermination.lambda$start$0(Test09.java:40)
    at java.lang.Thread.run(Thread.java:748)
09:56:01.624 c.TwoPhaseTermination [t1] - 料理后事

代码分析

  • TwoPhaseTermination类中调用start()方法,启动一个t1线程。
  • t1线程中while(true)方法循环执行监控记录,使得t1线程无法结束。
  • stop方法初始值为false,t1线程刚启动时,不会进入if(stop)条件判断中执行break;退出循环。
  • stop()方法中将成员变量stop改为true,同时调用monitorThread.interrupt();方法,为了打断正在睡眠中的t1线程,使其停止睡眠立即向下执行。
  • 要想使t1线程能够读取到成员变量stop在主线程中的更改,加上关键字volatile

执行过程分析

  • 主线程中调用TwoPhaseTermination类的start()方法,开启t1线程;
  • t1线程中每睡眠1s控制台输出一次“执行监控记录”;
  • 主线程在3.5秒后调用stop()方法,将stop设置成true,准备停止t1线程;
  • 此时t1线程已经执行完第三轮循环,正处于第四轮循环的睡眠状态,但是被monitorThread.interrupt();方法打断睡眠,抛出一个打断异常后,开始第五轮循环;
  • 此时stop成员变量为true,进入if判断条件,料理后事后退出循环;
  • t1线程运行结束,退出。

1.2 该方式存在的缺陷

当我们多次调用start()方法时,会创建多个t1线程,整个线程都在重复执行,消耗资源且没有意义。

public static void main(String[] args) throws InterruptedException {
    TwoPhaseTermination tpt = new TwoPhaseTermination();
    tpt.start();
    tpt.start();
    Thread.sleep(3500);
    log.debug("停止监控");
    tpt.stop();
}

运行结果

10:04:33.048 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:33.049 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:34.052 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:34.052 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:35.052 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:35.052 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:35.547 c.Test09 [main] - 停止监控
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.lilinchao.concurrent.demo_03.TwoPhaseTermination.lambda$start$0(Test09.java:41)
    at java.lang.Thread.run(Thread.java:748)
10:04:35.549 c.TwoPhaseTermination [t1] - 料理后事
10:04:36.053 c.TwoPhaseTermination [t1] - 执行监控记录
10:04:36.053 c.TwoPhaseTermination [t1] - 料理后事

问题?

如何保证某个方法被多次调用时,只会执行一次,下次在执行直接返回,不再继续向下执行。

可以通过Balking模式来实现。

二、Balking模式

2.1 定义

Balking (犹豫)模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,那么本线程就无需再做了,直接结束返回。

2.2 实现

Balking模式就是加入一个全局变量starting,通过starting的状态来判断该线程是否被调用过

private volatile boolean starting;

在调用方法中加入条件判断

if (starting) {
    return;
}
starting = true;
  • 当方法第一次被调用,starting变量为false,不进入条件判断语句,继续向下执行,这时将starting状态改为true;
  • 当方法已经被调用过时,此时状态值starting被改为true,进入到条件判断当中,执行return,退出该方法。

示例

public class MonitorService {
    // 用来表示是否已经有线程已经在执行启动了
    private volatile boolean starting;
    public void start() {
        log.info("尝试启动监控线程...");
        synchronized (this) {
            if (starting) {
                return;
            }
            starting = true;
        }
        // 真正启动监控线程...
    }
}

思考:为什么需要在Balking模式代码中加上synchronized锁?

if (starting) {
    return;
}
starting = true;

因为上方几段代码即涉及到了读,也涉及到了写,对于多行语句,在多线程情况下,保证的就不仅仅是可见性,必须得保证代码的原子性。所以,上方几段代码必须放在synchronized代码块中。

三、Balking模式应用

对上方volatile实现两阶段终止模式代码通过Balking模式进行改进

import lombok.extern.slf4j.Slf4j;
/**
 * Created by xiaowei
 * Date 2022/10/29
 * Description Balking模式应用
 */
@Slf4j(topic = "c.Test09")
public class Test09 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        tpt.start();
        tpt.start();
        /*Thread.sleep(3500);
        log.debug("停止监控");
        tpt.stop();*/
    }
}
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {
    // 监控线程
    private Thread monitorThread;
    // 停止标记
    private volatile boolean stop = false;
    // 判断是否执行过 start 方法
    private boolean starting = false;
    // 启动监控线程
    public void start() {
        synchronized (this){
            if (starting) {
                return;
            }
            starting = true;
        }
        monitorThread = new Thread(() -> {
            while (true) {
                //是否被打断
                if(stop){
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1");
        monitorThread.start();
    }
    //停止监控线程
    public void stop(){
        stop = true;
        monitorThread.interrupt();
    }
}

运行结果

10:44:22.543 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:23.546 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:24.547 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:25.547 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:26.561 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:27.561 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:28.562 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:29.562 c.TwoPhaseTermination [t1] - 执行监控记录
10:44:30.563 c.TwoPhaseTermination [t1] - 执行监控记录

从结果可以看出,每隔1s输出一次记录,不会再像之前一样,每秒打印多次记录。

目录
相关文章
|
5月前
|
运维 Serverless 数据处理
函数计算产品使用问题之遇到生成没有反应、中止也不行,以及刷新后队列积累的问题,该怎么办
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2月前
分布式事务的两阶段提交和三阶段提交分别有什么优缺点?
【9月更文挑战第9天】两阶段提交(2PC)和三阶段提交(3PC)是解决分布式系统事务一致性的机制。2PC实现简单,保证强一致性,但存在同步阻塞、单点故障和数据不一致风险。3PC通过引入超时机制减少阻塞时间,降低单点故障影响,但复杂性增加,仍可能数据不一致,并有额外性能开销。
|
3月前
|
算法
两阶段提交
【8月更文挑战第11天】
36 1
|
4月前
|
安全
线程操纵术并行策略问题之ForkJoinTask提交任务的问题如何解决
线程操纵术并行策略问题之ForkJoinTask提交任务的问题如何解决
|
存储 算法 关系型数据库
对比两阶段提交,三阶段提交做了哪些改进?
在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。在关系型数据库中,由于存在事务机制,可以保证每个独立节点上的数据操作满足 ACID。但是,相互独立的节点之间无法准确的知道其他节点中的事务执行情况,所以在分布式的场景下,如果不添加额外的机制,多个节点之间理论上无法达到一致的状态。 在分布式事务中,两阶段和三阶段提交是经典的一致性算法,那么两阶段和三阶段提交的具体流程是怎样的,三阶段提交又是如何改进的呢?
105 0
|
6月前
|
传感器 自然语言处理 自动驾驶
自动执行与反馈
自动执行与反馈
35 1
|
存储 算法 NoSQL
分布式事务两阶段提交和三阶段提交有什么区别?
分布式事务两阶段提交和三阶段提交有什么区别?
245 0
分布式事务两阶段提交和三阶段提交有什么区别?
|
设计模式 监控 Java
线程之两阶段终止模式(Two-Phase Termination Patter)模式
两阶段终止模式(Two-Phase Termination Pattern)是一种软件设计模式,用于管理线程或进程的生命周期。它包括两个阶段:第一阶段是准备阶段,该阶段用于准备线程或进程的停止;第二阶段是停止阶段,该阶段用于执行实际的停止操作。这种模式的主要目的是确保线程或进程在停止之前完成必要的清理和资源释放操作,以避免出现资源泄漏和数据损坏等问题。两阶段终止模式是一种常见的并发编程模式,广泛应用于Java和其他编程语言中。
190 0
线程之两阶段终止模式(Two-Phase Termination Patter)模式
|
存储 关系型数据库 分布式数据库
PostgreSQL 14中两阶段提交的逻辑解码
PostgreSQL 14中两阶段提交的逻辑解码
191 0
|
消息中间件 Java
《JUC并发编程 - 模式篇》保护性暂停模式 | 顺序控制模式 | 生产者消费者模式 | 两阶段终止模式 | Balking模式 | 享元模式(一)
《JUC并发编程 - 模式篇》保护性暂停模式 | 顺序控制模式 | 生产者消费者模式 | 两阶段终止模式 | Balking模式 | 享元模式
《JUC并发编程 - 模式篇》保护性暂停模式 | 顺序控制模式 | 生产者消费者模式 | 两阶段终止模式 | Balking模式 | 享元模式(一)