精华内容
下载资源
问答
  • Java线程就像一个虚拟CPU,可以在Java应用程序中执行Java代码。当Java应用程序启动时,它的main()方法由主线程执行,主线程是Java虚拟机为运行应用程序而创建的一个特殊线程。你可在应用程序内部创建和启动更多的...

    作者:Jakob Jenkov,2020-03-29
    翻译:GentlemanTsao,2020-4-26


    Java线程就像一个虚拟CPU,可以在Java应用程序中执行Java代码。当Java应用程序启动时,它的main()方法由主线程执行,主线程是Java虚拟机为运行应用程序而创建的一个特殊线程。你可在应用程序内部创建和启动更多的线程,这些线程可以与主线程并行执行应用程序的部分代码。

    和其他Java对象一样,Java线程也是对象。线程是java.lang.Thread类的实例,或者是该类的子类的实例。除了作为对象之外,java线程还可以执行代码。在本Java线程教程中,我将讲解如何创建和启动线程。

    创建和启动线程

    在Java中创建线程的过程如下:

      Thread thread = new Thread();

    要启动Java线程,需要调用其start()方法,如下所示:

      thread.start();

    这个例子没有指定线程要执行的代码。因此,线程启动后会立即停止。
    有两种方法可以指定线程应该执行的代码。第一种方法是创建线程的子类并重写run()方法。第二种方法是实现Runnable(java.lang.Runnable)的对象,并将该对象传递给线程构造函数。下面将介绍这两种方法。

    Thread子类

    指定线程该运行什么代码的第一种方法,是创建Thread的子类并重写run()方法。run()方法是线程在调用start()之后执行的操作。下面是创建Java Thread子类的示例:

      public class MyThread extends Thread {
    
        public void run(){
           System.out.println("MyThread running");
        }
      }

    要创建和启动上述线程,可以执行以下操作:

      MyThread myThread = new MyThread();
      myTread.start();

    一旦线程启动,start()调用就返回了,它不会等到run()方法完成。run()方法就像是在另一个CPU中执行一样。当run()方法执行时,它将输出文本“MyThread running”。

    你还可以创建Thread的一个匿名子类,如下所示:

      Thread thread = new Thread(){
        public void run(){
          System.out.println("Thread Running");
        }
      }
    
      thread.start();

    这个例子将在新线程执行run()方法,并输出文本“Thread running”。

    实现Runnable接口

    指定线程该运行什么代码的第二种方法,是创建一个实现java.lang.Runnable接口的类。实现Runnable接口的Java对象可以由Java Thread执行。本教程之后会演示这种实现方法。

    Runnable接口是Java平台附带的标准Java接口。Runnable接口只有一个run()方法。以下是Runnable接口的基本代码:

    public interface Runnable() {
    
        public void run();
    
    }

    无论线程在执行时应该做什么,都必须包含在run()方法的实现中。有三种方法可以实现Runnable接口:
    1.创建一个实现Runnable接口的Java类。
    2.创建一个实现Runnable接口的匿名类。
    3.创建一个实现Runnable接口的Java Lambda。
    这三个选项将在下面的章节中进行说明。

    Java类实现Runnable

    实现Java Runnable接口的第一种方法是创建自己的Java类来实现Runnable接口。下面是实现Runnable接口的自定义Java类的示例:

      public class MyRunnable implements Runnable {
    
        public void run(){
           System.out.println("MyRunnable running");
        }
      }

    这个Runnable的实现所做的是打印出文本“MyRunnable running”。打印该文本后,run()方法退出,运行run()方法的线程也将停止。

    Runnable的匿名实现

    你还可以创建Runnable的匿名实现。下面是实现Runnable接口的匿名Java类的示例:

    Runnable myRunnable =
        new Runnable(){
            public void run(){
                System.out.println("Runnable running");
            }
        }

    除了是一个匿名类之外,这个示例与使用自定义类实现Runnable接口的示例非常相似。

    Runnable的Java Lambda实现

    实现Runnable接口的第三种方法是创建Runnable接口的Java Lambda实现。之所以能这么做是因为Runnable接口只有一个未实现的方法,因此它实际上(尽管可能并非特意)是一个功能性Java接口。

    下面是实现Runnable接口的Java lambda表达式的示例:

    Runnable runnable =
            () -> { System.out.println("Lambda Runnable running"); };

    使用Runnable启动线程

    要让run()方法由线程执行,可以实现Runnable接口的类、匿名类或lambda表达式,并将它们的实例传递给Thread的构造函数。下面是具体做法:

    Runnable runnable = new MyRunnable(); // 或者是匿名类,Lambda...
    
    Thread thread = new Thread(runnable);
    thread.start();

    当线程启动时,它将调用MyRunnable实例的run()方法,而不是执行它自己的run()方法。上面的例子会输出文本“MyRunnable running”。

    用Thread子类还是用Runnable?

    没人规定这两种方法中哪一种最好。两种方法都有效。不过,就我个人而言,我更喜欢实现一个Runnable,并将该实现的实例传给Thread实例。当线程池执行Runnable时,很容易将Runnable实例排队,直到池中的线程空闲。这对于Thread子类来说有点困难。

    有时你可能既需要实现Runnable又需要实现Thread子类。例如,假如要创建可以执行多个Runnable的Thread子类。在实现线程池时通常会出现这种情况。

    常见陷阱:调用run()而不是start()

    创建和启动线程时,常见的错误是调用线程的run()方法而不是start(),如下所示:

      Thread newThread = new Thread(MyRunnable());
      newThread.run();  //应该是start();

    起初,你可能不会察觉到有什么不对,因为Runnable的run()方法执行的跟你预想的一样。但是,它不是由你刚刚创建的新线程执行的。相反,run()方法由创建线程的线程执行,也就是执行上述两行代码的线程。要让新创建的线程newThread调用MyRunnable实例的run()方法,必须调用newThread.start()方法。

    线程名称

    当你创建一个Java线程时,你可以给它起个名字,用来帮助你区分不同的线程。例如,如果多个线程写入System.out,则可以方便地查看是哪个线程写入了文本。下面是一个例子:

       Thread thread = new Thread("New Thread") {
          public void run(){
            System.out.println("run by: " + getName());
          }
       };
    
    
       thread.start();
       System.out.println(thread.getName());

    注意字符串“New Thread”作为参数传递给Thread构造函数。此字符串是线程的名称。该名称可以通过线程的getName()方法获得。用Runnable实现时,也可以将名称传递给线程。像这样:

       MyRunnable runnable = new MyRunnable();
       Thread thread = new Thread(runnable, "New Thread");
    
       thread.start();
       System.out.println(thread.getName());

    但是请注意,由于MyRunnable类不是线程的子类,因此它不能访问执行它的线程的getName()方法。

    Thread.currentThread()

    Thread.currentThread()方法返回一个引用,该引用指向在执行currentThread()的线程实例。这样你就可以访问在执行特定代码块的Java线程对象。下面是使用Thread.currentThread()的示例:

    Thread thread = Thread.currentThread();

    一旦有了对线程对象的引用,就可以对其调用方法。例如,可以获取当前执行代码的线程的名称,如下所示:

       String threadName = Thread.currentThread().getName();

    Java线程示例

    这里有一个小例子。首先,它打印出执行main()方法的线程的名称。该线程由JVM分配。然后启动10个线程,并给它们一个数字作为名称("" + i)。之后每个线程都会打印出其名称,然后停止执行。

    public class ThreadExample {
    
      public static void main(String[] args){
        System.out.println(Thread.currentThread().getName());
        for(int i=0; i<10; i++){
          new Thread("" + i){
            public void run(){
              System.out.println("Thread: " + getName() + " running");
            }
          }.start();
        }
      }
    }

    要注意的是,即使线程是按顺序(1、2、3等)启动的,它们也未必依次执行,也就是线程1未必是第一个将其名称写入System.out的线程。这是因为线程原则上是并行执行的,而不是顺序执行的。JVM和操作系统分别或共同决定线程的执行顺序。此顺序不必与它们启动时的顺序相同。

    暂停线程

    线程可以通过调用静态方法thread.sleep()暂停自身。sleep()以毫秒为参数。sleep()方法将尝试休眠指定毫秒后恢复执行。Thread的sleep()虽不是100%精确,但已经很好了。下面的示例通过调用Thread的sleep()方法暂停Java线程3秒(3.000 millliseconds):
    (译者注:原文错误,实际应是暂停10秒,即10,000毫秒)

    try {
        Thread.sleep(10L * 1000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    执行上述Java代码的线程将休眠大约10秒(10.000毫秒)。

    停止线程

    想要停止Java线程,需要在线程的实现代码中做些准备工作。Java Thread类包含stop()方法,但已弃用。原始的stop()方法无法保证线程被停止后会处于何种状态。也就是说,线程在执行期间访问的所有Java对象都将处于未知状态。如果应用程序中的其他线程也访问了同样的对象,则应用程序可能会出现意料之外和不可预知的错误。

    不要调用stop()方法,你必须自己实现停止线程的代码。下面是一个实现Runnable的类的示例,该类包含一个额外方法doStop(),用于向Runnable发出停止的信号。Runnable将检查此信号,并在准备好时停止。

    public class MyRunnable implements Runnable {
    
        private boolean doStop = false;
    
        public synchronized void doStop() {
            this.doStop = true;
        }
    
        private synchronized boolean keepRunning() {
            return this.doStop == false;
        }
    
        @Override
        public void run() {
            while(keepRunning()) {
                // 持续做线程该做的工作
                System.out.println("Running");
    
                try {
                    Thread.sleep(3L * 1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    }

    注意doStop()和keepRunning()方法。doStop()是用于给别的线程调用的,而不是由执行MyRunnable的run()方法的线程调用。keepRunning()方法由执行MyRunnable的run()方法的线程在内部调用。只要没有调用doStop(),keep running()方法就会返回true,这意味着执行run()方法的线程将继续运行。

    下面的示例启动了一个Java线程,该线程执行上述MyRunnable类的实例,并在延迟后停止线程:

    public class MyRunnableMain {
    
        public static void main(String[] args) {
            MyRunnable myRunnable = new MyRunnable();
    
            Thread thread = new Thread(myRunnable);
    
            thread.start();
    
            try {
                Thread.sleep(10L * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            myRunnable.doStop();
        }
    }

    本例首先创建一个MyRunnable实例,然后将该实例传递给一个线程并启动该线程。之后执行main()方法的线程(也就是主线程)休眠10秒,然后调用MyRunnable实例的doStop()方法。这会让执行MyRunnable方法的线程停止,因为调用doStop()后,keepRunning()将返回true。

    请记住,如果你的Runnable要实现的不仅仅是run()方法(例如,还有stop()或pause()方法),那么你就不能再使用Java lambda表达式创建你的Runnable实现。Java lambda只能实现一个方法。相应的,你必须使用自定义类,或自定义一个继承Runnable的接口,该接口需要有额外的方法,并且由匿名类实现。

    每天点个赞,让自己进步一点点
    下一篇:
    java并发和多线程教程(八):竞态条件和临界区

    更多阅读:
    系列专栏:java并发和多线程教程2020版

    展开全文
  • Java停止线程执行的方法

    万次阅读 2015-06-27 15:51:29
    Java停止线程执行的方法作者:chszs,转载需注明。博客主页:http://blog.csdn.net/chszs一、暂停或停止线程的理论在Java编程中,要暂停或停止当前正在运行的线程,有几种方法。对于把线程转入睡眠Sleep状态,使用...

    Java中停止线程执行的方法

    作者:chszs,转载需注明。博客主页:http://blog.csdn.net/chszs

    一、暂停或停止线程的理论

    在Java编程中,要暂停或停止当前正在运行的线程,有几种方法。对于把线程转入睡眠Sleep状态,使用Thread.sleep()是最正确的方式。或许有人会问,为什么不使用等待wait()或通知notify()?要知道,使用等待或通知都不是很好的方式。
    线程可以使用等待wait()实现被阻塞,这属于条件等待的方式,当条件满足后,又会从阻塞转为等待状态。尽管可以在等待wait()条件那里放一个超时设置,但等待wait()的设计目的不是这样的,等待wait()在设计上是用于Java线程间的通信。
    这里写图片描述

    而使用睡眠sleep()方式,可以让线程从当前开始睡眠指定的时间。注意不要使用睡眠sleep()方式去代替等待wait()或通知notify(),反之亦然。
    等待wait()或通知notify()不应该用于暂停线程,还有一个原因,等待wait()或通知notify()需要一个锁。只能从一个同步的方法或同步的代码块去调用它们,获取锁和释放锁的开销是比较大的。而且,只是暂停线程的话,无需引入锁机制。
    sleep()与wait()还有一点不同,sleep()会把当前的线程转入等待状态,它不会释放它持有的任何锁,而wait()使得线程转入阻塞状态,会释放掉自己持有的锁。
    总之,Java多线程编程并不简单,即使是简单的任务,如创建线程、停止线程或暂停线程,都需要认真掌握Java API。

    二、暂停或停止线程的实战

    下面的例子中,要暂停线程,可以使用Thread.sleep()或TimeUnit.sleep()方法。例子中,有两个线程,主线程由JVM启动,它执行main()方法。第二个线程叫T1,它由主线程创建,用于循环运行游戏。我们传递的Runnable任务是一个无限循环,会一直运行直到我们停止它。注意看,我使用了volatile关键字。
    主线程首先启动T1线程,再使用stop()方法停止线程。
    在这个例子中,我们有两种方法停止线程的运行,使用Thread.sleep()方法或者是使用TimeUnit.sleep()方法。TimeUnit类既可以指定秒即TimeUnit.SECONDS,又可以指定毫秒即TimeUnit.MILLISECONDS。总的来说,使用TimeUnit的sleep()方法,使得代码更为易读。

    import static java.lang.Thread.currentThread;
    import java.util.concurrent.TimeUnit;
    public class ThreadPauseDemo{
        public static void main(String args[]) throws InterruptedException {
            Game game = new Game();
            Thread t1 = new Thread(game, "T1");
            t1.start();
            // 现在停止Game线程
            System.out.println(currentThread().getName() + " is stopping game thread");
            game.stop();
            // 查看Game线程停止的状态
            TimeUnit.MILLISECONDS.sleep(200);
            System.out.println(currentThread().getName() + " is finished now");
        }
    }
    
    class Game implements Runnable{
        private volatile boolean isStopped = false;
        public void run(){
            while(!isStopped){
                System.out.println("Game thread is running......");
                System.out.println("Game thread is now going to pause");
                try{
                    Thread.sleep(200);
                } catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("Game thread is now resumed......");
            }
            System.out.println("Game thread is stopped......");
        }
        public void stop(){
            isStopped = true;
        }
    }
    

    程序输出如下:

    Game thread is running......
    main is stopping game thread
    Game thread is now going to pause
    Game thread is now resumed......
    Game thread is stopped......
    main is finished now
    

    注:
    volatile关键字:当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取这个值时,它会去主内存中读取新值。volatile关键字保证了可见性。普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
    通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
    一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么它就具备了两层语义:

    1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
    2. 禁止进行指令重排序。

    volatile关键字禁止指令重排序有两层意思:

    1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。
    2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
      volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。

    三、sleep()方法总结

    Thread.sleep()方法可以让线程暂停或停止,它还有一些细节需要注意:

    1. Thread.sleep()方法是一个静态方法,它总是可以让当前的线程转入睡眠。
    2. 可以调用interrupt()方法把当前睡眠的线程唤醒。
    3. sleep()方法不能保证线程能精准地在指定那一毫秒内转入睡眠,它的精度取决于系统的计时器。
    4. 它不会释放它所获得的锁。
    展开全文
  • Java 多线程】Java 如何停止线程

    千次阅读 2017-03-04 20:48:04
    总结如下几种方式使线程停止执行: (1)共享bool类型变量方式; (2)调用线程interrupt方式,同时通过isInterrupted方式判断线程是否被停止,若停止则终止循环操作(线程类中循环外没有其他操作); (3)调用...

    总结如下几种方式使线程停止执行:
    (1)共享bool类型变量方式;
    (2)调用线程interrupt方式,同时通过isInterrupted方式判断线程是否被停止,若停止则终止循环操作(线程类中循环外没有其他操作);
    (3)调用线程interrupt方式 + 抛出异常方式(循环外有其他操作情况);
    (4)调用线程interrupt方式 + return方式(循环外有其他操作情况);
    (5)将线程设置为守护线程,则所有非守护线程停止时,守护线程便结束;
    下面对以上五种方式分别进行详细解释。

    方法一:采用bool类型共享变量的方式
    子线程与主线程共享一个实例对象,该对象中包含一个bool类型变量,可以在主线程中对该bool类型变量进行赋值,从而在子线程循环中通过判断该值确定是否继续;

    注意:当有多个线程共享该变量时,考虑是否采用volatile关键字修饰该变量,保证所有线程读取到的变量为最新值;
    参考代码如下:

    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class MyObject {
        volatile public boolean flag = true;
    }
    
    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class ThreadTest implements Runnable{
    
        MyObject myObject = null;
    
        public ThreadTest(MyObject myObject){
            this.myObject = myObject;
        }
    
        @Override
        public void run() {
    
            while (myObject.flag){
    
            }
            System.out.println(Thread.currentThread().getId() + " 线程退出!");
        }
    }
    
    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class Main {
        public static void main(String[] args) {
            MyObject myObject = new MyObject();
    
            new Thread(new ThreadTest(myObject)).start();
            try {
                Thread.sleep(1000);
                myObject.flag = false;//停止线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class Main {
        public static void main(String[] args) {
            MyObject myObject = new MyObject();
    
            new Thread(new ThreadTest(myObject)).start();
            try {
                Thread.sleep(1000);
                myObject.flag = false;//停止线程
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    最后运行结果为:
    这里写图片描述

    方法二:调用线程interrupt方式,同时通过isInterrupted方式判断线程是否被停止
    与方法一类似,当线程循环外无其他语句时,外部调用该线程的interrupt方法,线程内部通过判断isInterrupted方式判断线程是否被停止从而终止循环;
    参考代码如下:

    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class ThreadTest extends Thread{
    
    
    
        @Override
        public void run() {
    
            while (!this.isInterrupted()){
    
            }
            System.out.println(Thread.currentThread().getId() + " 线程退出!");
        }
    }
    
    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class Main {
        public static void main(String[] args) {
    
            Thread thread = new ThreadTest();
            thread.start();
            try {
                Thread.sleep(1000);
                thread.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    运行结果为:

    这里写图片描述
    方法三:当线程内部循环外仍有其他语句时,采用上述两种方法只能退出循环,而不能阻止循环外的语句执行。此时我们可以采取抛出异常的方式。当线程通过isInterrupted函数判断线程已被停止后,可以抛出异常InterruptedException来终止整个线程的执行。
    参考代码如下:

    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class ThreadTest extends Thread{
    
        @Override
        public void run() {
            try {
                int i = 5000000;
                while (i>0){
                    System.out.println(i);
                    if (this.isInterrupted()){
                        System.out.println(Thread.currentThread().getId() + " 线程退出!");
                        throw new InterruptedException();
                    }
                    i--;
                }
                System.out.println(Thread.currentThread().getId() + " 线程继续执行");
    
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println(Thread.currentThread().getId() + " 抛出异常,进入catch代码块");
            }
    
        }
    }
    package study20170304;
    
    /**
     * Created by apple on 17/3/4.
     */
    public class Main {
        public static void main(String[] args) {
    
            Thread thread = new ThreadTest();
            thread.start();
            try {
                Thread.sleep(1000);
                thread.interrupt();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    此时程序运行结果为:
    这里写图片描述

    方法四:与方法三相同,只不过可以不抛出异常,而是直接return;但是《Java多线程编程核心技术》一书中写道:不过还是建议使用“抛异常”法来实现线程的停止,因为在catch块中可以对异常的信息进行相关的处理,而且实用异常流能更好、更方便的控制程序的运行流程,不至于代码中出现很多个return;造成污染。

    方法五:将一个线程设置为守护线程后,当进程中没有非守护线程后,守护线程自动结束;
    如下代码,在main函数中将ThreadTest线程设置为守护线程,则main函数所在主线程执行完毕后,守护线程也结束;
    参考代码如下:

    package study20170304;
    /**
     * Created by apple on 17/3/4.
     */
    public class Main {
        public static void main(String[] args) {
    
            Thread thread = new ThreadTest();
            thread.setDaemon(true);
            thread.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    展开全文
  • Java如何停止线程

    千次阅读 2018-10-25 11:28:24
    启动一个线程或任务都是很简单,线程一般在任务结束后便会自行停止。但是有时我们希望能够在线程自行停止前能够停止它们,比如一些取消操作,或者是应用程序需要快速关闭。博主日前就遇到了这样的问题。 但是在...

    Preface

    启动一个线程或任务都是很简单,线程一般在任务结束后便会自行停止。但是有时我们希望能够在线程自行停止前能够停止它们,比如一些取消操作,或者是应用程序需要快速关闭。博主日前就遇到了这样的问题。

    但是在《JAVA并发编程实践》一书中指出:

    Java没有提供任何机制,来安全地强迫停止手头地工作。

     

    一般来讲,对于Runnable来说,需要做的任务都是在run方法里面进行的,停止一个线程也可以认为是从run方法跳出来吧。

    线程停止

    使用标志位

    这种方式的基本思想为:使用一个标志位,通过这个标志位来决定run方法是继续执行还是直接结束。如下:

      static class CancelledTaggedRunnnable implements Runnable{
            private volatile boolean cancelled = false;
            @Override
            public void run() {
                while(!cancelled){
                 //没有停止需要做什么操作
                }
                //线程停止后需要干什么
                System.out.println("任务结束了");
            }
            public void cancell(){
                cancelled = true;
            }
        }

    定义一个标志位:cancelled,cancelled为true是即run方法结束,反之继续while里面的任务。通过cancell方法来停止任务。

    写一个测试类:

     @Test
        public void testCancelledTaggedRunnnable() throws InterruptedException {
            CancelledTaggedRunnnable taggedRunnnable = new CancelledTaggedRunnnable();
            Thread thread = new Thread(taggedRunnnable);
            thread.start();
    
            System.err.println(thread.isAlive());
    
            Thread.sleep(1000);
            taggedRunnnable.cancell();
    
            Thread.sleep(1000);
            System.err.println(thread.isAlive());
    
            Thread.sleep(1000);
            System.err.println(thread.isAlive());
        }

    上述代码的操作为:启动该线程,1s秒调用cancell方法,从而来停止该线程。结果如下

     

     

    中断策略

    但是使用标志位这种方法有个很大的局限性,那就是通过循环来使每次的操作都需要检查一下标志位。

    Java还提供了中断协作机制,能够使一个线程要求另外一个线程停止当前工作。其大致的思想为:调用线程Thread的interrupt方法,在线程内部通过捕获InterruptedException异常来决定线程是否继续还是退出。如下:

    static class InterruptRunnable implements Runnable{
    
            private BlockingQueue queue = new ArrayBlockingQueue(10);
            @Override
            public void run() {
                int i= 0;
                for (;;) {
                    try {
                        //线程的操作
                        i++;
                        queue.put(i);
                    } catch (InterruptedException e) {
                        //捕获到了异常 该怎么做
                        System.out.println(queue);
                        e.printStackTrace();
                        return;
                    }
                }
            }
        }

    上述代码通过BlockingQueue的put方法来抛出InterruptedException异常。当内部捕获到该异常时,从而决定是否继续还是直接退出了。

    写个测试方法:

    @Test
        public void testInterruptRunnable() throws InterruptedException {
            InterruptRunnable runnable = new InterruptRunnable();
            Thread thread = new Thread(runnable);
            thread.start();
    
            System.err.println(thread.isAlive());
    
            Thread.sleep(1000);
            thread.interrupt();
    
            Thread.sleep(1000);
            System.err.println(thread.isAlive());
    
            Thread.sleep(1000);
            System.err.println(thread.isAlive());
        }

    该测试方法大致同使用标志位的测试方法,同样启动该线程后,1秒后调用线程的interrupt方法,从而触发Runnable的内部queue.put(i)操作抛出InterruptedException异常。   测试结果如下:

    强行停止(不推荐)

    对于多线程,使用中断策略 较为优雅,也是官方的推荐。线程停止前你应该做一些操作,从而保证该线程运行后的数据不会被丢失,这一点在多线程中极为重要。

    但是对于中断策略还是有一个很大的缺陷,那就是,必须通过中断的阻塞函数,如:我使用的BlockingQueue的put方法,也可以是Thread.sleep()方法,才能抛出InterruptedException。如果抛不出这样的异常呢?

    对于单线程,还有一个简单粗暴的方式,那就是Java已经不再使用的Thread  stop方法。

    使用方法即直接调用:thread.stop()即可。

    如果你对该线程的操作不再关心了,对结果也不再在意了,使用该方法也是可以的。

    但多线程情况下,或者线程带锁的情况下 那就要慎用了。该方法不安全

    使用future任务

     

    Java还提供一个可带返回值的线程操作,FutureTask。在这个类中可以调用其cancel方法来取消这个任务。

    你可以设置true或者false来决定是否让线程出现中断的操作。从而可以使这个中断后能够捕获InterruptedException来决定是否让操作结束。

    一般来说,这个类都是跟带返回值的Callable接口一起使用,Runnable接口也是可以使用的,定义线程的操作:

    static class MyRunnable implements Runnable{
            @Override
            public void run(){
                for(;;){
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        System.out.println("线程被中断");
                        return;
                    }
                    System.out.println("程序运行中....");
                }
            }
        }

    上述代码较简单,不需要多说了。使用FutureTask进行测试,先不让产生中断:

     @Test
        public void test01() throws Exception {
            FutureTask task = new FutureTask<>(new MyRunnable(), 0);
            ExecutorService pool = Executors.newFixedThreadPool(1);
            pool.submit(task);
    
            Thread.sleep(1000);
            System.out.println("任务是否完成?"+task.isDone());
            System.out.println("任务是否被取消?"+task.isCancelled());
    
            Thread.sleep(1000);
            //在这里取消操作
            System.out.println(task.cancel(true));
    
            Thread.sleep(1000);
            System.out.println("任务是否完成?"+task.isDone());
            System.out.println("任务是否被取消?"+task.isCancelled());
    
            Thread.sleep(5000);
    
        }

    通过线程池的方式来提交任务,让其运行,2秒后取消任务,查看控制台:

    但是FutureTask的cancelle方法并不能真正的停止当前线程,其主要思想还是如同中断策略。也就是说,如果我们不设置task.cancell(true),那么任务还不会被中断,如,我们改为task.cancell(false),结果如下:

    虽然此时调用了task.cancell(false)方法,但是通过isDone,isCancelled方法并不能说名任务就结束了。如上,任务还在继续。

     

    综上,Java并没有停止线程的方法,如果需要手动的停止,还是建议较优雅的中断策略,或者FutureTask。 

     

    展开全文
  • 关于Java停止线程执行的方法总结

    千次阅读 2015-11-12 22:15:34
    如何停止java的线程一直是开发多线程程序的一个非常头痛的问题,本文我们就来讨论一下关于Java停止线程执行的方法,我们应该如何正确停止java中的线程。 Java停止线程执行的方法 一、暂停或停止线程的...
  • http://blog.csdn.net/ssssssue 停止线程 测试interrupt中断线程 测试interrupt中断线程 public class Demo { public static void main(String[] args) { StopRunnable stopRunnable = new Stop
  • Java线程Runnable

    2017-11-03 21:01:14
    参考 https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html ...https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html https://docs.oracle.com/javase
  • 去tm点个赞
  • Java 如何正确停止线程

    千次阅读 2020-07-12 16:35:50
    如何正确停止线程? 文章目录如何正确停止线程?前言项目环境1.使用场景2.为什么不强制停止?而是通知、协作3.如何用 interrupt 停止线程?4.sleep 期间能否感受到中断?4.1 示例4.2 子方法签名抛异常,run() 强制 ...
  • java多线程之启动,停止线程

    千次阅读 2016-10-19 22:34:33
    停止线程 线程的优先级 守护线程 线程的启动 启动线程的两种方式 继承Thread类 线程代码: package com.sun.thread; public class MyThread extends Thread { @Override public void run() { super
  • 文章目录线程可能被阻塞如果线程在每次迭代时都阻塞 线程可能被阻塞 子线程sleep的过程中, 给出中断信号的demo 当子线程正在休眠的过程中, 去进行线程的中断. 因此主线程要等子线程执行到 Thread.sleep(1000);这一行...
  • Java停止线程及有锁时停止方法

    千次阅读 2017-01-07 16:15:51
    那就是让run方法结束要知道开启多线程运行,其运行代码通常都是循环结构,只要控制住循环就可以让run方法结合苏,也就是线程结束比如写个代码class StopThread implements Runnable { public synchroniz
  • 线程的启动和停止简介 线程的启动 方法一 实现Runnable接口,重写run()函数,运行start()方法 Runnable run = new Runnable() { @Override public void run() { while(true && !Thread.currentThrea...
  • 主要是 Java 并发, 停止线程的方法分析
  • 主要讲解线程中断,以及停止线程的方式,并且进行了相关的代码演示。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,074
精华内容 24,429
关键字:

Javarunnable停止线程