多线程设计模式——Two-phase Termination(两阶段终止)模式

这些都是根据我最近看的《Java实战指南多线程编程(设计模式篇)》所得整理。

模式名称

Two-phase Termination(两阶段终止)模式

模式面对的问题

在多线程中,线程会因为各种原因停止,但这并不简单。首先Java没有提供直接的API用于停止线程。而且停止线程的时候还有一些问题需要处理,比如线程的信息保存问题。所以需要有程序优雅的停止线程。

解决思路

解决这个问题分为两个时间短,第一个时间段是准备阶段,这个阶段通知目标线程,使得线程开始进行相关处理。第二时间段是执行阶段,检查准备阶段设置的线程停止标志和信号,在次基础上决定线程停止的时机,并进行适当的“清理”操作。
根据这个思路创建类ThreadOwner目标线程的拥有者,Terminatable类可以停止线程的抽象,AbstractTerminatableThread类可操纵停止的线程,TerminationToken类存放线程停止标志,ConcreteTerminatableThread类有应用自己实现的
AbstractTerminatableThread参与者的实现类。
下面是时序图

Created with Raphaël 2.1.0 Client Client ThreadOwner ThreadOwner abstractTerminatableThread abstractTerminatableThread terminationToken TerminationToken terminationToken TerminationToken 1shotdown() 2terminate() 3setToShutdown() 4 5doTerminate 6interrupt()

例子代码

这个代码是结合上一个设计模式不可变对象模式中的代码进行编写的,具体的执行类没有写,因为具体的执行逻辑是随着实际的需求变化而变化的
AlarmMgr类(相当于ThreadOwner)

public class AlarmMgr {
    //保存AlarmMgr类的唯一实例
    private static final AlarmMgr INSTANCE = new AlarmMgr();
    private volatile boolean shutdownrequested = false;
    //告警发送线程
    private final AlarmSendingThread alarmSendingThread;

    //私有构造器
    private AlarmMgr(){
        alarmSendingThread = new AlarmSendingThread();
    }

    //返回类AlarmMgr的唯一实例
    public  static AlarmMgr getInstance(){
        return INSTANCE;
    }

    //发送告警
    public int sendAlarm(AlarmType type,String id,String extraInfo){
        Debug.info("Trigger alarm "+ type + "," +id + "," + extraInfo);
        int duplicateSubmissionCount = 0;
        try{
            AlarmInfo alarmInfo = new AlarmInfo(id,type);
            alarmInfo.setExtraInfo(extraInfo);
            duplicateSubmissionCount = alarmSendingThread.sendAlarm(alarmInfo);
        }catch(Throwable t){
            t.printStackTrace();
        }
        return duplicateSubmissionCount;
    }

    public void init(){
        alarmSendingThread.start();
    }

    puclic synchronized void shutdown(){
        if(shutdownRequested){
            throw new IllegalStateExecption("shutdown already requested!");
        }
        alarmSendingThread.terminate();
        shutdownRequested = true;
    }
}

AlarmSendingThread类

public class AlarmSendindThread extends AbstractTerminatable{
    private final AlarmAgent alarmAgent = new AlarmAgent();
    //告警队列
    private final ConcurrentMap<AlarmInfo> alarmQueue;
    private final ConcurrentMap<String,AtomicInteger> submittedAlarmRegistry;
    public AlarmSending(){
        alarmQueue = new ArratBlockingQueue<AlarmInfo>(100);
        submittedAlarmRegistry = new ConcurrentHashMap<String,AtomicInteger>();
        alarmAgent.init();
    }

    @Override
    protected void doRun() throws Exception{
        AlarmInfo alarm;
        alarm = alarmQueue.take();
        terminationToken.reservations.decrementAndGet();
        try{
            //讲告警信息发送至告警服务器
            alarmAgent.sendAlarm(alarm);
        }catch(Exception e){
            e.printStackTrace();
        }
        /*处理恢复告警:讲相应的故障告警从注册表中删除,
         * 使得相应故障恢复后若再次出现相同故障,
         * 该古装信息能够上报到服务器
         */
        if(AlarmType.RESUME == alarm.type){
            String key = AlarmType.FAULT.toString() +":" + alarm.getId() + '@'
              + alarm.getExtraInfo();
            submittedAlarmRegistry.remove(key);
            key = AlarmType.RESUNE.toString() + ':' + alarm.getId() +'@'
              +alarm.getExtraInfo();
            submittedAlarmRegistry.remove(key);
        }
    }

    puclic int sendAlarm(final AlarmInfo alarmInfo){
        AlarmType type = alarmInfo.type;
        String id = alarmInfo.getId();
        String extraInfo = alarmInfo.getExtraInfo();

        if(terminationToken.isShundown){
            //记录告警
            System.err.println("rejected alarm:" + id + "," + extraInfo);
            return -1;
        }
        int duplicateSubmissionCount = 0;
        try{
            AtomicInteger prevSubmittedCounter;
            prevSubmittedCounter = submittedAlarmRegistry.putIfAbsent(
                    type.toString()+';'+id+'@'+extraInfo,new AtomicInteger(0));
            if(null == prevSubmittedCounter){
                terminationToken.reservations.incrementAndGet();
                alarmQueue.put(alarmInfo);
            }else{
                duplicateSubmissionCount = prevSubmittedCounter.incrementAndGet();
            }
        }catch(Throwable t){
            t.printStackTrace();
        }
        return duplicateSubmissionCount;
    }

    protected void doCleanup(Exception exp){
        if(null != exp && !(exp instanceof InterruptedException)){
            exp.printStackTrace();
        }
        alarmAgent.disconnect();
    }
}

AbstrackTerminatableThread类

public class AbstractTerminatableThread extends Thread implements Terminable{
    //模式角色:Two-phaseTermination.TerminationToken
    public final TerminationToken terminationToken;

    public AbstractTerminatableThread(){
        this(new TerminationToken());
    }

    //线程间共享的线程终止标志实例
    public AbstractTerminatableThread(TerminationToken terminationToken){
        super();
        this.terminationToken = terminationToken;
        terminationToken.register(this);
    }

    //留给子类实现其线程处理处理逻辑
    protected abstract void doRun()throw Exception;

    //留给子类实现。用于实现线程停止后的一些清理动作。
    protected void doCleanuo(Exception cause){
        //什么也不做
    }

    //留给子类实现。用于执行线程停止所需的操作
    protected void doTerminiate(){
        //什么也不做
    }

    @Override
    public void run(){
        Exception ex = null;
        try{
            for(;;){
                //在执行线程的处理逻辑钱先判断线程停止的标志。
                if(terminationToken.isToShutdown()&&terminationToken.reservations.get()<=0){
                    break;
                }
                doRun();
            }
        }catch(Exception e){
            ex = e;
        }finally{
            try{
                doCleaup(ex);
            }finally{
                terminationToken.notifyThreadTermination(this);
            }
        }
    }

    @Override
    public void interrupt(){
        terminate();
    }

    //请求停止线程
    @Override
    public void terminate(){
        terminationToken.setToShutdown(true);
        try{
            doTerminiate();
        }finally{
            //若无待处理的任务,则试图强制终止线程
            if(terminationToken.reservations.get()<=0){
                super.interrupt();
            }
        }
    }

    public void terminate(boolean waitUtilThreadTerminated){
        terminate();
        if(waitUtilThreadTerminated){
            try{
                this.join();
            }catch(InterruptedException e){
                Thread.currentThread().interrupt();
            }
        }
    }
}

TerminationToken类

public class TerminationToken {
    protected volatile boolean toShutdown = false;
    public final AtomicInteger reservations = new AtomicInteger(0);

    /*在多个可停止线程实例共享一个TerminationToken实例的情况下,
     * 该队列用于记录那些共享TerminationToken实例的可停止线程,
     * 一遍尽可减少锁的使用的情况下,实现这些线程的停止。
     */
    private final Queue<WeakReference<Terminatable>> coordinatedThreads;

    public TerminationToken(){
        coordinatedThreads = new ConcurrentLinkedQueue<WeakReference<Terminatable>>();
    }

    public boolean isToShutdown(){
        return toShutdown;
    }

    protected void setToShutdown(boolean toShutdown){
        this.toShutdown = true;
    }

    protected void register(Terminatable thread){
        coodinatedthreads.add(new WeakReference<Terminatable>(thread));
    }

    //通知TerminationToken实例:共享该实例的所有可停止线程中的一个线程停止了,
    //一遍其停止其他未被停止线程
    protected void notifyThreadTermination(Terminatable thread){
        WeakReference<Terminatable> wrThread;
        Terminatable otherThread;
        while(null != (wrThread = coordinaterThreads.poll())){
            otherThread = wrThread.get();
            if(null != otherThread && otherThread != thread){
                otherThread.terminate();
            }

        }
    }
}

模式评价

这个模式是需要安全性高的线程都要用到的,如果精简的话,模式也可以很简单

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值