如何正确停止线程

如何正确停止线程

新建线程
新建线程的方式有多种
  • 实现Runnable接口,重写run()方法
  • 继承Thread类,重写run()方法
  • 实现callable接口,重写call方法
  • 使用线程池
  • 使用定时器
代码举例-Runnable接口

Runnable接口重写run方法

public class NewRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("I am NewRunnable Thread");
    }

    public static void main(String[] args) {
        NewRunnable newRunnable = new NewRunnable();
        Thread thread = new Thread(newRunnable);
        thread.start();
    }
}

// 结果
/*
    I am NewRunnable Thread
*/

上述代码中新建NewRunnable 类实现了Runnable接口后重写了run方法,实现新建一个线程,可能这样体现不出来多线程,可以加个循环。

public class NewThread  {

    public static void main(String[] args) {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(200);
                        System.out.println("i am "+ i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        });
        thread.start();
        try {
            Thread.sleep(500);
            System.out.println("i am mainThread");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行结果

i am 0
i am 1
i am mainThread
i am 2
i am 3
i am 4

使用了匿名内部类重写了Runnable接口run方法,然后循环五次,每次睡眠200毫秒,主线程中睡眠500毫秒,所以当子线程循环两次后,在第三次打印中间主线程开始打印。

代码举例-Thread类

继承Thread类

public class NewThread extends Thread {

    @Override
    public void run() {
        System.out.println("I am NewThread!");
    }

    public static void main(String[] args) {
        NewThread newThread = new NewThread();
        newThread.start();
    }
}
/*
   I am NewThread!
*/
新建线程总结

无论是实现Runnable接口还是继承Thread类,发现最终都是通过Thread类的start()方法开启的线程,而不是通过run()方法,看一下run方法和start方法的源代码

run()方法
public class Thread implements Runnable {
    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

发现Thread也实现了Runnable接口,并重写了run方法,如上所示,我们在使用实现Runnable接口,并把对象当作参数传给Thread类中 public Thread(Runnable target);构造器时,这时候run()方法执行的就是传入对象的run()方法,run()方法中也只有这三行代码,所以并没有实现多线程。

start()方法
public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

可以看到首先run()方法if (threadStatus != 0) 判断线程状态,而后调用了start0方法,而start0方法private native void start0();可以看到是一个native方法,并非是由java实现

搜索后得知

Java中,Thread类是用于实现多线程编程的类。当我们创建一个新线程并启动它时,实际上是调用了Thread类中的start()方法,该方法会执行一个本地方法start0()start0()方法会启动一个新的系统线程,并调用run()方法。run()方法是线程执行的代码,当run()方法执行完毕后,线程就会自动结束。

start0()方法是一个本地方法(native method),它是由JVM实现的。本地方法是指使用C/C++等低级语言编写的方法,它们通常由JVM加载并在本地系统上执行。start0()方法在底层实现了线程的创建和启动,其具体实现会依赖于不同的操作系统和JVM实现。在JVM中,start0()方法的实现是由native层面提供的,因此我们无法直接查看其源代码。

所以我们可以得知,在java中真正实现多线程的是Thread类中的start()方法。

补充 callable接口
public class NewCallable implements Callable {
    @Override
    public String call() throws Exception {
        String s = "I am newCallable!";
        System.out.println(Thread.currentThread().getName());

        return s;
    }

    public static void main(String[] args) {
        NewCallable newCallable = new NewCallable();

        FutureTask futureTask = new FutureTask<>(newCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Object o = futureTask.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("i am mainThread");
        System.out.println(Thread.currentThread().getName());


    }
}

/*
    Thread-0
    I am newCallable!
    i am mainThread
    main
*/

可以看到实现callable接口也是通过Thread类创建的新线程,不同的是使用了FutureTask类接收了返回值

简要说一下futureTask可以看到该类间接继承了Runnable接口,所以依然可以理解成callable接口就是实现Runnbale接口,重写了run方法的方式实现了多线程,其他方式不在过多阐释。

停止线程
如何停止线程?
  • stop()?
  • destroy()?
  • suspend()?
  • interrupt()?

查看 API,我们会看到 java.lang.Thread 类型提供了一系列的方法如 start()、stop()、resume()、suspend()、destroy()等方法来管理线程。但是除了 start() 之外,其它几个方法都被声名为已过时(deprecated)

详细原因可看jdk文档的解释

https://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

正确停止线程的方式是使用interrupt()方法

Interrupt()方法

在 Java 中,Thread.interrupt() 方法用于中断线程,其作用是设置线程的中断标志位为 true。被中断的线程可以通过检查自身的中断标志位来判断是否被中断,然后执行适当的操作。

如果线程被阻塞在某些操作上(如等待 I/O 操作、sleep() 等),调用 interrupt() 方法会中断该线程的阻塞状态,抛出 InterruptedException 异常,并清除中断标志位。如果线程没有被阻塞,则调用 interrupt() 方法只是设置线程的中断标志位为 true,线程仍然可以继续运行。

举个例子
public class MyThread extends Thread {
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("MyThread is running...");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println("MyThread is interrupted!");
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.interrupt();
    }
}

// 结果
/*
    MyThread is running...
    MyThread is running...
    MyThread is running...
    MyThread is running...
    MyThread is running...
    MyThread is interrupted!
*/

新建了一个线程,在主线程中,我们睡眠5S后将新建线程中断,这是新线程捕获中断,并打印了MyThread is interrupted!

中断标志位

上方代码中,如果我们把while (!Thread.currentThread().isInterrupted()) 换成 while (true)发现运行结果是一样的,为什么呢?这就牵扯到中断标志位了,如上方所言 **如果线程被阻塞在某些操作上(如等待 I/O 操作、sleep() 等),调用 interrupt() 方法会中断该线程的阻塞状态,抛出 InterruptedException 异常,并清除中断标志位。**上方代码中5s中断线程时,线程在sleep状态,这时候程序抛出 InterruptedException 异常,并清除中断标志位。在抛出异常时线程已经停止了。

public class MyThread extends Thread {
    public void run() {
            int i =0;
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("正在打印:"+ ++i);
            }

    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        int sum = 0;
        for (int i = 0; i < 1000000; i++) {
            sum += i;
        }
        myThread.interrupt();
    }
}


// 结果
/*
正在打印:0
正在打印:1
正在打印:2
正在打印:3
........
正在打印:104
*/

可以看到我们通过判断程序的中断信号来执行代码,当程序被中断时,跳出while循环,新线程运行结束

循环中使用注意事项

如果我们将try catch 放在循环中发现程序响应中断后并未停止,这是因为sleep()清除了中断标志位,所以不能将try catch语句放在循环中,而应该放在循环外。

public class MyThread extends Thread {
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                System.out.println("MyThread is running...");
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.interrupt();
    }
}

结果

/*
MyThread is running...
MyThread is running...
MyThread is running...
MyThread is running...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at threadToStop.MyThread.run(MyThread.java:8)
MyThread is running...
MyThread is running...
    */
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值