Java多线程学习(十一)中断机制学习

在实际应用中,如果想停掉某个线程,那么可以调用线程的stop()方法或者suspend()方法来强制性的停止某个线程。但是这种方法是很粗暴的,它是强制性的停止线程,这样可能会导致线程之前工作的数据丢失,所以Java已经摒弃了这些强制终止的方法。

取而代之的是一种中断机制,interrupt()方法。在JVM中每个线程都有一个boolean类型的线程是否中断的标志,调用线程中断的方法只是将这个标志置为true,这是一种优雅的线程中断的方法,它告诉线程要中断了,而不是强制中断此线程。

线程在运行时需要检测这个标志位的状态,如果不检测,即使调用了中断方法线程也不会中断。

我们可以通过调用Thread.currentThread().isInterrupted()或者Thread.interrupted()来检测线程的中断标志是否被置位。这两个方法的区别是Thread.currentThread().isInterrupted()是线程对象的方法,调用它后不清除线程中断标志位;而Thread.interrupted()是一个静态方法,调用它会清除线程中断标志位。

中断非阻塞线程

比如,让主线程休眠一秒钟,保证线程1执行一段时间,然后在主线程中中断线程1,在线程1中用过检测标志位来查看线程是否该中断。如果中断了,保存计算结果。

public class InterruptTest2 {
    public static void main(String[] args) {
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println("线程还在执行...");
                    for (; i < 1000000000; i++) {
                    }
                }
                if (Thread.currentThread().isInterrupted()){
                    System.out.println(i);
                }
            }
        });
        thread2.start();
        try {
            Thread.sleep(1000);
            thread2.interrupt();
            System.out.println("主线程中断线程1");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


中断阻塞线程

对于一些可阻塞的方法,比如sleep(),wait(),join()等方法,如果我们不用中断机制,而是自己去设置取消方法,那么当线程永久堵塞时,也就不能去检测标志位,那么线程就永远停不下来了。但是通过interrupt,这些可阻塞方法在JVM内部会检测interrupt标志位,当发现中断时,会抛出InterruptedException异常,来终止阻塞方法。

public class InterruptTest {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"正在执行");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        //由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去。保留中断证据
                        Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                    if (Thread.currentThread().isInterrupted()){
                        System.out.println(Thread.currentThread().getName()+"被中断了");
                    }
                }
            }
        });
        thread.start();
        try {
            System.out.println("主线程睡眠");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程中断其他线程");
        thread.interrupt();
        //线程一旦被中断,isInterrupted()方法便会返回true,而一旦sleep()方法抛出异常,它将清空中断标志,此时isInterrupted()方法将返回false。
        System.out.println("是否被中断1:"+thread.isInterrupted());
        System.out.println("主线程执行完成");
    }
}


对于非阻塞方法的中断,也可以通过抛出InterruptedException异常来传递中断。


不可中断线程

有一种情况是线程不能被中断的,比如调用synchronized关键字和reentrantLock.lock()获取锁的过程。输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态。

public class InterruptThreadTest5 {
	
    public void deathLock(Object lock1, Object lock2) {
        try {
            synchronized (lock1) {
            	System.out.println(Thread.currentThread().getName()+ " is running");
            	// 让另外一个线程获得另一个锁
                Thread.sleep(10);
                // 造成死锁
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        } catch (InterruptedException e) {
        	System.out.println(Thread.currentThread().getName()+ " is interrupted");
            e.printStackTrace();
        }
    }
	
	public static void main(String [] args) { 
		
		final InterruptThreadTest5 itt = new InterruptThreadTest5();
		final Object lock1 = new Object();
		final Object lock2 = new Object();
		Thread t1 = new Thread(new Runnable(){
			public void run() {
				itt.deathLock(lock1, lock2);
			}
		},"A"); 
		Thread t2 = new Thread(new Runnable(){
			public void run() {
				itt.deathLock(lock2, lock1);
			}
		},"B"); 
		
		t1.start();
		t2.start();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 中断线程t1、t2
		t1.interrupt();
		t2.interrupt();
	}
}



猜你喜欢

转载自blog.csdn.net/wanderlustlee/article/details/80851759