java多线程:Two-Phase-Termination模式 (分阶段)请求线程中断模式

引子
1.为什么不用stop方法

不能使用Thread类的stop方法,使用stop方法,线程的安全性无法得到保证,即使是线程中使用了synchronized方法。

public synchronized void setXY(int newX,int newY){
	x=newX;
	y=newY
}

上边的例子用synchronized保证了只能一个线程访问setXY代码段,x y被一个线程先后更新。但是用stop方法,就会出现x被赋值为newX后,直接退出,这样y还没被赋值。

2.Interrupt方法的含义

Object.wait 、Thread sleep 、join 可以抛出InterruptedException,其他任何线程都可以调用任意线程的interrupt()方法,而不用获取该线程的锁,对wait的线程,调用interrupt后会重新获得锁然后抛出InterruptException异常。InterruptException异常是因为线程的中断状态发生了改变,线程检测到该变化抛出异常。抛出异常后中断状态被清除。
try catch 捕获到InterruptedException,中断状态被清除,执行的线程中就会忽略中断请求,判断中断标记isInterrupted就会无效。因此有下边几种情况。

响应中断请求的线程

package com.example.test;

// 在run方法中有sleep的两种写法
class HelloThread extends Thread {
    public void run() {
// 这种写法是廖大神博客中的一种写法
        int n = 0;

        while (!isInterrupted()) {// 有Thread.sleep时判断中断,需要用break跳出循环
            n++;
            System.out.println("HelloThread执行!");

            try {
                Thread.sleep(100);// 当前线程正在等待Thread.sleep()等待,如果对其调用中断请求,就抛出InterruptedException异常,
                // 就说明其他线程调用了此线程的interrupt方法,当前线程结束状态会被清除,必须跳出循环
            } catch (InterruptedException e) {// 抛出异常会清除中断状态
                System.out.println("HelloThread 线程在睡眠或者等待状态同时调用了该线程的interrupt方法,收到中断请求引发异常,捕获之后标志位重置为非中断状态");
                System.out.println("HelloThread"+isInterrupted());// 捕获了异常isInterrupted()就不会变成true了
                break;// 需要用break跳出循环
            } finally {
               System.out.println("HelloThread"+isInterrupted());// 捕获了异常isInterrupted()就不会变成true了
            }
        }

// 这种写法是java核心编程第一卷中的写法
      // try {
      //  	while(true){
      //  	    n++;
      //  		System.out.println("HelloThread执行");
      //      	Thread.sleep(10000);// 当前线程正在等待Thread.sleep()等待,如果对其调用中断请求,就抛出InterruptedException异常
	  //		}
      //  }
      //  catch (InterruptedException e){
      //      System.out.println("HelloThread 线程在睡眠或者等待状态同时调用了该线程的interrupt方法");
      //  }finally {
      //      System.out.println("HelloThread "+isInterrupted());
      //  }
    }
}

// 在run方法中没有sleep的写法
class HelloThread1 extends Thread {
    public void run() {
        int n = 0;

        try {
            while (!isInterrupted()) {

                n++;
                System.out.println(n + "HelloThread1 hello!");
                System.out.println(Thread.currentThread().getName() + "HelloThread1 没有收到中断请求");
            }
        } catch (Exception e) {
            System.out.println("HelloThread1发生异常");
        } finally {
            System.out.println("HelloThread1 最终结果");
        }

    }
}
// 如果是join,测试中断
class MyThread extends Thread {
    public void run() {
        HelloThread helloThread = new HelloThread();
        helloThread.start();
        try {
            System.out.println( "MyThread 执行");
            helloThread.join();//  让当前进程等待,当前线程正在等待,抛出异常,就说明其他线程调用了此线程的interrupt方法,该线程就该被中断,并引发异常,进入catch语句中,并且isInterrupted被清除,状态为false;
            System.out.println("MyThread wait");
        } catch (InterruptedException e) {

            System.out.println("MyThread wait 正在等待被中断了,验证中断异常isInterrupted被清除,状态为false"+isInterrupted());// 验证isInterrupted被清除,状态为false
        }
        helloThread.interrupt();// 引发HelloThread中的InterruptedException异常
    }
}

public class TestThread {

    public static void main(String[] args) throws InterruptedException {

        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        // myThread的中断状态isInterrupted()置为true,
        //如果myThread正在等待或者阻塞状态(如调用currentThread.sleep,ortherThread.join,object.wait),
        //对myThread调用interrupt(),会引发MyThread类中的异常
        myThread.interrupt();
        System.out.println("main wait");
        myThread.join();
        System.out.println("end");
    }
}

知识点:
Tip1:volatile 原子操作赋值

java中规定对基本类型和引用类型的赋值和引用是原子操作(atom),但是对long和double类型没有约束,所以对long和double的赋值和引用,涉及到线程不安全,加上volatile 即 volatile long a=0L;

Tip2:volatile在单例模式下也有应用

// volatile在单例模式下也有应用
class VolatileSingleton {  
    private volatile static VolatileSingleton singleton;  
    private VolatileSingleton (){}  
    public static VolatileSingleton getSingleton() {  
    	if (singleton == null) {  
        	synchronized (VolatileSingleton.class) {  
        		if (singleton == null) {  
            		singleton = new Singleton();  
        		}  
        	}  
    	}  
    	
    	return singleton;  
    }  
}

Tip3:join和isAlive方法
join和isAlive方法,可以使用thread.join等待线程执行结束,也可以使用t.isAlive判断线程是否终止。或者使用thread.getState来判断,不过使用isAlive会更好。

中断线程的另外一种方式:volatile关键字修饰变量,作为中断请求标记,但是如果有sleep wait join不能立刻响应

// 廖大神博客中的另一种写法,使用volatile关键字保证读取的时效性,volatile修饰的成员变量Bealean型,可以保证一旦有线程改变了runningState,该线程马上能读到,并停止线程
class InterpretThreadDemo extends Thread {
    public volatile Boolean runningState = true;

    public void run() {
        while (runningState) {
            System.out.println("i am running!");
        }
    }
}

假设一个线程执行方法中不能判断是否有sleep wait等方法,有没有一种通用的方式来中断线程呢?

解决这个难题的方法在 《java图解多线程模式》有了解答,将两种方式结合起来:

1.仅仅检查中断状态是不够的,如果出现了
try{Thread.sleep(100);}catch(InterrupedException e){// 忽略InterruptedException}
这样的语句。即使判断了isInterrupted()也会被catch执行后清除中断状态(忽略中断),而导致判断isInterrupted()无必要也没用处。(和java核心编程中描述一致)。
2.仅仅检查被volatile修饰的停止请求状态标记是不够的,因为当线程正在sleep时,线程中断请求发出后,线程不会立即响应,而是sleep时间到再响应。如果线程执行,遇到wait,线程不会立即从等待队列中出来进行响应。时效性不行。

响应中断请求的线程

package com.example.TwoPhaseTermination;

public class WonderfulInterruptThread extends Thread{
    private long count=0;// 计数

    // 中断标记,指示是否发出中断请求,将interruptRequestFlag中断标记位封装起来,用方法赋值和获取
    private volatile boolean interruptRequestFlag=false;

    public void wonderfulInterrupt(){
        interruptRequestFlag=true;// 保证没有sleep、wait、join时立即终止
        interrupt();// 有sleep、wait、join时立即终止
    }

    public boolean isInterruptRequest(){// interruptRequestFlag,是否发出中断请求
        return interruptRequestFlag;
    }

    public final void run(){
        try {
            while (!interruptRequestFlag){
                doWork();
            }
        }catch (InterruptedException e){

        }finally {
            doFinalWork();
        }
    }

    private void doWork() throws InterruptedException {// 循环一直做的工作
        count++;
        System.out.println("一直累加至 "+count);
        Thread.sleep(500);
    }

    private void doFinalWork(){// 线程结束前,一定要执行的
        System.out.println("最终输出累加数值 "+count);
    }
}

主线程发出中断请求

package com.example.TwoPhaseTermination;

public class Main {
    public static void main(String[] args) {
        System.out.println("主线程开始");

        try {
            WonderfulInterruptThread t=new WonderfulInterruptThread();

            t.start();
            System.out.println("WonderfulInterruptThread执行");
            System.out.println("主线程休眠");
            Thread.sleep(6000);// 主线程休眠

            System.out.println("主线程对WonderfulInterruptThread线程发出中断请求");
            t.wonderfulInterrupt();

            // 等待WonderfulInterruptThread结束
//            t.join();
            while (t.isAlive()){

            }
            System.out.println("WonderfulInterruptThread响应中断,执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程结束");
    }
}

结果
主线程开始
WonderfulInterruptThread执行
主线程休眠
一直累加至 1
一直累加至 2
一直累加至 3
一直累加至 4
一直累加至 5
一直累加至 6
一直累加至 7
一直累加至 8
一直累加至 9
一直累加至 10
一直累加至 11
一直累加至 12
主线程对WonderfulInterruptThread线程发出中断请求
最终输出累加数值 12
WonderfulInterruptThread响应中断,执行完毕
主线程结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值