多线程中的停止

一、什么是多线程中的停止?

每个线程都有一个中断状态,默认的中断状态都是 false,一个线程可以给另一个线程发送一个中断信号,接收到这个中断信号的线程的中断状态就被置为 true,Java 中的 Thread 类提供了以下几种方法来获取和修改线程的中断状态:

  • void interrupt():将某个线程的中断状态置为 true,不是真正的将线程停止,而是在当前线程中打了一个停止的标记
  • static boolean interrupted():静态方法,测试当前线程是否已经中断,执行后会将状态标志清除为 false
  • boolean isInterrupted():测试线程是否已经中断,不会清除状态标志

二、使用停止方法

1. 使用interrupt、interrupted方法
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("i = " + (i + 1));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
        //对线程 thread-0 调用 interrupt 方法
        thread.interrupt();

        //测试当前线程是否中断,此时当前线程是 main
        System.out.println("当前线程是:" + Thread.currentThread().getName());
        System.out.println("停止的线程是:" + thread.getName());
        System.out.println("interrupted 1:" + thread.interrupted());
        System.out.println("interrupted 2:" + thread.interrupted());
    }
}

结果是:

当前线程是:main
i = 1
停止的线程是:Thread-0
i = 2
interrupted 1false
i = 3
interrupted 2false
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

对线程 Thread-0 调用 interrupt 方法,interrupt 方法并不能暂停线程,线程 Thread-0 依然执行完了。两个 interrupted 方法输出的结果都是 false,这是因为,interrupted 方法的意思是:测试当前线程是否已经中断,当前的线程是 main,因为没有对线程 main 执行 interrupt 方法,因此输出都是 false;而停止的线程是 Thread-0,而 main 这个线程并没有停止,因此输出都是 false

如果在 run 方法里面加上下面的语句,表示测试当前线程(Thread-0)是否被中断

System.out.println(Thread.currentThread().interrupted());

结果打印为 true

再来看一个例子,此时我们使用 interrupt 来停止当前线程 main

public class MyThread extends Thread {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        Thread.currentThread().interrupt();
        System.out.println("是否停止 1 ? = " + Thread.interrupted());
        System.out.println("是否停止 2 ? = " + Thread.interrupted());
    }

}

结果是:

main
是否停止 1 ? = true
是否停止 2 ? = false

当前线程是 main,同时停止当前线程 main,然后用 interrupted() 方法判断当前线程(main)是否中断了,第1个说明停止了,但是第2个却是false。

这是为什么呢?这是因为,interrupt 方法具有清除状态的功能,即第一次用完之后,会清除中断的状态,因此第一输出 true,第二次输出 false

2. 使用isInterrupted()方法

将第一个例子主方法中的 interrupted 方法都替换成 isInterrupted 方法

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("i = " + (i + 1));
        }
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        thread.interrupt();

        //测试当前线程是否中断,此时当前线程是 main
        System.out.println("当前线程是:" + Thread.currentThread().getName());
        System.out.println("停止的线程是:" + thread.getName());

        System.out.println("isInterrupted 1:" + thread.isInterrupted());
        System.out.println("isInterrupted 2:" + thread.isInterrupted());
    }
}

结果是:

扫描二维码关注公众号,回复: 5647650 查看本文章
当前线程是:main
停止的线程是:Thread-0
i = 1
isInterrupted 1true
isInterrupted 2true
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

依然对线程 Thread-0 调用 interrupt 方法,isInterrupted 方法用来判断调用该方法的线程(Thread-0)是否已经中断,可见都已经中断了,同时,isInterrupted 方法不会清除状态标记,因此,可以输出两个 true

3.suspend、resume和stop方法的使用

虽然这三个方法都已经是过时方法,但是也可以来了解一下

  • suspend():使线程暂停,但是不会释放锁
  • resume():使暂停的线程恢复,这里的暂停指的是调用suspend而被暂停的线程
  • stop():停止当前线程,释放占有的锁,同时也是线程不安全的

三、中断遇上阻塞

1.使用interrupt的情况

如果接受中断信号的线程,即调用 interrupt 方法的对象所在的那个线程,因为调用了某些方法而被阻塞,比如调用了 Thread 类的 join、sleep、或者 Object 类的 wait 方法,那么就会抛出异常

join、sleep 和 wait 每一个方法都会抛出 InterruptedException 异常

@throws  InterruptedException
if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown

大概说的是,一个线程在调用这些方法之前或者阻塞过程中都会监测自己的中断状态是否为 true,如果为 true,立即返回并且抛出一个 InterruptedException 的异常,而且还会清除该线程的中断状态,即把中断状态由 ture 改为 false

换一个例子来理解下

public class MyThread extends Thread {

    @Override
    public void run() {
        try {
            Thread.sleep(200000000);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()
                    + " 抛出异常,中断状态是:" + Thread.currentThread().isInterrupted());
        }
        System.out.println(Thread.currentThread().getName()
                + " 的中断状态是:" + Thread.currentThread().isInterrupted());
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
        System.out.println("执行了");
        thread.interrupt();

        Thread.sleep(2000);

        System.out.println("interrupted 1:" + thread.isInterrupted());
        System.out.println("interrupted 2:" + thread.isInterrupted());
    }
}

结果是:

执行了
Thread-0 抛出异常,中断状态是:false
Thread-0 的中断状态是:false
interrupted 1false
interrupted 2false

此时线程 Thread-0 因为调用了 Thread.sleep(long) 方法而被阻塞,同时在线程 main 中调用 thread.interrupt() 方法,向线程 Thread-0 发送一个中断信号,并把该线程的中断状态置为 true,线程 Thread-0 会立马抛出一个 InterruptedException 异常,接着会把线程 Thread-0 的中断状态清除,变为 false,所以无论在线程 Thread-0 还是 main 中都输出 false

正因为对线程调用了 interrupt 方法,使得原本要阻塞 200000s 的线程直接抛出异常,即没有再被阻塞,该规则对于 wait 和 join 方法同样适用,即如果在调用这些方法前,已经被 interrupt 了,那么就会立即抛出异常,不用阻塞

2. 使用lockInterruptibly的情况

lockInterruptibly() 方法表示,如果当前线程未被中断,则获取该锁定,如果当前已经被中断则出现异常,同样也是抛出 InterruptedException 异常

public void lockInterruptibly() throws InterruptedException

该异常的解释如下:

@throws InterruptedException if the current thread is interrupted

如同 sleep、join 和 wait 方法一样,如果一个线程因为调用 lockInterruptibly 方法在等待 lock 锁的过程中发送阻塞,此时另一个线程调用 interrupt 方法向该线程发送中断信号的话,则发生阻塞的那个线程会立即返回,同时抛出一个 InterruptedException 异常,并且会清除中断状态。看了例子把

class MyService {

    public ReentrantLock lock = new ReentrantLock();

    public void waitMethod() {
        try {
            System.out.println(Thread.currentThread().getName()
                    + " 是否被中断:" + Thread.currentThread().isInterrupted());
          
            lock.lockInterruptibly();

            try {
                System.out.println("lock begin " + Thread.currentThread().getName());
                Thread.sleep(5000);
                System.out.println("lock end " + Thread.currentThread().getName());
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            System.out.println("lock catch" + Thread.currentThread().getName()
                    + " " + Thread.currentThread().isInterrupted());
        }
    }

}

public class Run {

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                service9.waitMethod();
            }
        };
        Thread thread = new Thread(runnable);
        thread.setName("AAA");
        thread.start();

        Thread.sleep(2000);

        Thread thread1 = new Thread(runnable);
        thread1.setName("BBB");
        thread1.start();

        System.out.println("做标记前:" + thread1.isInterrupted() + System.currentTimeMillis());
        thread1.interrupt();            //做个标记

        Thread.sleep(3000);

        System.out.println("做完标记后:" + thread1.isInterrupted() + System.currentTimeMillis());
    }

}

结果是:

AAA 是否被中断:false
lock begin AAA
做标记前:false
BBB 是否被中断:true
lock catchBBB false
lock end AAA
做完标记后:false

线程 AAA 先获取 lock 锁,然后被阻塞,在阻塞的过程中,线程 BBB 调用 lockInterruptibly 方法尝试获取 lock 锁,由于此时锁被线程 AAA 占有,因此线程 BBB 也被阻塞,此时在线程 main 中调用 thread1.interrupt() 向线程 BBB 发送一个中断信号,此时线程 BBB 的中断状态变为 true(从输出也可以看到),因为,线程 BBB 立刻抛出 InterruptedException 异常,并把中断状态清除,变为 false,最好的输出都是 false 了

四、参考

《Java多线程编程核心技术》
https://mp.weixin.qq.com/s?__biz=MzIxNTQ3NDMzMw==&mid=2247483879&idx=1&sn=5167ed2e7b1804f741fac39318e657aa&scene=19#wechat_redirect

猜你喜欢

转载自blog.csdn.net/babycan5/article/details/84031201