广告位联系
返回顶部
分享到

Java线程的停止实现原理介绍

java 来源:未知 作者:佚名 发布时间:2023-09-21 23:09:23 人浏览
摘要

线程停止的原理 使用interrupt来通知,而不是强制 java提供了interrrupt让一个线程来通知另一个线程停止 如果想中断一个线程,但是那个线程不想去中断,那就无能为力,我们没有强制去

线程停止的原理

使用interrupt来通知,而不是强制

java提供了interrrupt让一个线程来通知另一个线程停止

如果想中断一个线程,但是那个线程不想去中断,那就无能为力,我们没有强制去中断线程的手段,因为线程停止前需要做一定的收尾工作

所以正确停止线程,是如何用interrupt来通知那个线程,以及被停止的线程如何进行配合

如何正确停止线程

在普通情况下停止线程

代码展示

  • 调用interrupt没有作用
  • 下面这段代码,执行interrupt之后,线程并没有被中断
  • 因为被执行的线程并没有相应中断的方式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public class stopThreadWithoutSleep  implements Runnable{

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

        Thread thread = new Thread(new stopThreadWithoutSleep());

        thread.start();

        Thread.sleep(1000);

        thread.interrupt();

    }

    @Override

    public void run() {

        int num = 0;

        while(num <= Integer.MAX_VALUE / 2) {

            if (num % 10000 == 0) {

                System.out.println(num + "是10000的倍数");

            }

            num++;

        }

        System.out.println("结束");

    }

}

/*

由于太长,只展示结尾部分的结果

1073710000是10000的倍数

1073720000是10000的倍数

1073730000是10000的倍数

1073740000是10000的倍数

结束

* */

  • 被执行线程加上相应中断的操作之后
  • 结果可知,被执行线程相应一秒之后就结束了

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public class stopThreadWithoutSleep  implements Runnable{

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

        Thread thread = new Thread(new stopThreadWithoutSleep());

        thread.start();

        Thread.sleep(1000);

        thread.interrupt();

    }

    @Override

    public void run() {

        int num = 0;

        while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {

            if (num % 10000 == 0) {

                System.out.println(num + "是10000的倍数");

            }

            num++;

        }

        System.out.println("结束");

    }

}

/*

由于太长,只展示结尾部分的结果

587830000是10000的倍数

587840000是10000的倍数

587850000是10000的倍数

587860000是10000的倍数

结束

* */

在阻塞情况下停止线程

代码展示

  • 中断之后,抛出异常
  • 线程在sleep的过程中,会catch到InterruptedException这个异常,从而相应中断

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

public class stopThreadWithSleep {

    public static void main(String[] args) {

        Runnable runnable = () -> {

            int num = 0;

            while (num <= 300 && !Thread.currentThread().isInterrupted()) {

                if (num % 100 == 0) {

                    System.out.println(num + "是100的倍数");

                }

                num++;

            }

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        };

        Thread thread = new Thread(runnable);

        thread.start();

        try {

            Thread.sleep(500);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        thread.interrupt();

    }

}

/*

* 0是100的倍数

100是100的倍数

200是100的倍数

300是100的倍数

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.stopThreadWithSleep.lambda$main$0(stopThreadWithSleep.java:15)

   at java.lang.Thread.run(Thread.java:748)

* */

线程在每次迭代后都阻塞

  • 代码展示 即使不在while判断是否中断,sleep也能中断异常

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

public class stopThreadWithSleepEveryLoop {

    public static void main(String[] args) {

        Runnable runnable = () -> {

            int num = 0;

            try {

                while (num <= 10000) {

                    if (num % 100 == 0) {

                        System.out.println(num + "是100的倍数");

                    }

                    num++;

                    Thread.sleep(10);

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        };

        Thread thread = new Thread(runnable);

        thread.start();

        try {

            Thread.sleep(5000);

        } catch (

                InterruptedException e) {

            e.printStackTrace();

        }

        thread.interrupt();

    }

}

/*

* 0是100的倍数

100是100的倍数

200是100的倍数

300是100的倍数

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.stopThreadWithSleepEveryLoop.lambda$main$0(stopThreadWithSleepEveryLoop.java:15)

   at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

*

* */

当catch写到while内,则不能正常中断

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

public class CantInterrupt {

    public static void main(String[] args) {

        Runnable runnable = () -> {

            int num = 0;

            while (num <= 10000) {

                if (num % 100 == 0) {

                    System.out.println(num + "是100的倍数");

                }

                num ++;

                try {

                    Thread.sleep(10);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        Thread thread = new Thread(runnable);

        thread.start();

        try {

            Thread.sleep(5000);

        } catch (

                InterruptedException e) {

            e.printStackTrace();

        }

        thread.interrupt();

    }

}

/*

* 0是100的倍数

100是100的倍数

200是100的倍数

300是100的倍数

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)

   at java.lang.Thread.run(Thread.java:748)

400是100的倍数

500是100的倍数

600是100的倍数

700是100的倍数

800是100的倍数

900是100的倍数

Process finished with exit code -1

* */

  • 即使在while的判断条件中,加上检测中断的机制,也不能正常中断
  • 因为java的sleep函数,一旦相应中断,就会将中断的标志位删除

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public class CantInterrupt {

    public static void main(String[] args) {

        Runnable runnable = () -> {

            int num = 0;

            while (num <= 10000 && !Thread.currentThread().isInterrupted()) {

                if (num % 100 == 0) {

                    System.out.println(num + "是100的倍数");

                }

                num++;

                try {

                    Thread.sleep(10);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        };

        Thread thread = new Thread(runnable);

        thread.start();

        try {

            Thread.sleep(5000);

        } catch (

                InterruptedException e) {

            e.printStackTrace();

        }

        thread.interrupt();

    }

}

/*

0是100的倍数

100是100的倍数

200是100的倍数

300是100的倍数

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)

   at java.lang.Thread.run(Thread.java:748)

400是100的倍数

500是100的倍数

Process finished with exit code -1

* */

停止线程的最佳实践

  • 在方法签名中抛出异常,在run方法中强制进行try catch

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

public class StopThreadInProd implements Runnable{

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

        Thread thread = new Thread(new StopThreadInProd());

        thread.start();

        Thread.sleep(1000);

        thread.interrupt();

    }

    @Override

    public void run() {

        while (true) {

            System.out.println("start");

            try {

                throwInMethod();

            } catch (InterruptedException e) {

                System.out.println("保存日志/关闭程序");

                e.printStackTrace();

            }

        }

    }

    private void throwInMethod() throws InterruptedException {

            Thread.sleep(2000);

    }

}

/*

* start

保存日志/关闭程序

start

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.StopThreadInProd.throwInMethod(StopThreadInProd.java:26)

   at com.jx.JavaTest.stopThread.StopThreadInProd.run(StopThreadInProd.java:17)

   at java.lang.Thread.run(Thread.java:748)

start

start

Process finished with exit code -1

*

* */

  • 在catch语句中调用Thread.currentThread().interrupt恢复中断状态
  • 结果:抛出异常,程序结束

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

public class StopThreadInProd2 implements Runnable{

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

        Thread thread = new Thread(new StopThreadInProd2());

        thread.start();

        Thread.sleep(1000);

        thread.interrupt();

    }

    @Override

    public void run() {

        while (true) {

            if (Thread.currentThread().isInterrupted()) {

                System.out.println("Interrupt");

                break;

            }

            reInterrupt();

        }

    }

    private void reInterrupt() {

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            Thread.currentThread().interrupt();

            e.printStackTrace();

        }

    }

}

/*

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)

   at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)

   at java.lang.Thread.run(Thread.java:748)

Interrupt

*

* */

  • 依照上面的代码,如果方法没有没有重新抛出异常
  • 结果:程序抛出异常,但是程序没有停止运行

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public class StopThreadInProd2 implements Runnable{

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

        Thread thread = new Thread(new StopThreadInProd2());

        thread.start();

        Thread.sleep(1000);

        thread.interrupt();

    }

    @Override

    public void run() {

        while (true) {

            if (Thread.currentThread().isInterrupted()) {

                System.out.println("Interrupt");

                break;

            }

            reInterrupt();

        }

    }

    private void reInterrupt() {

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

//            Thread.currentThread().interrupt();

            e.printStackTrace();

        }

    }

}

/*

java.lang.InterruptedException: sleep interrupted

   at java.lang.Thread.sleep(Native Method)

   at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)

   at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)

   at java.lang.Thread.run(Thread.java:748)

*

* */

错误停止的方法

被弃用的stop,suspend和resume方法

  • 使用stop停止线程,会导致线程运行一半突然停止,没办法完成最基本的操作,会造成脏数据
  • 下面这段代码的结果会造成一个连队只有部分人领取到了装备
  • stop是不安全的,会直接停止监视器
  • suspend和resume不会破坏对象,但是会让线程挂起,不释放锁,容易造成死锁

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

public class StopThread implements Runnable{

    @Override

    public void run() {

        // 模拟指挥军队,一共五个连队,每个连队一百人

        // 以连队为单位发放武器

        for (int i = 0; i < 5; i++) {

            System.out.println("连队" + i + "领取武器");

            for (int j = 0; j < 10; j++) {

                System.out.println(j);

                try {

                    Thread.sleep(50);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            System.out.println("连队" + i + "领取完毕");

        }

    }

    public static void main(String[] args) {

        Thread thread = new Thread(new StopThread());

        thread.start();

        try {

            thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        thread.stop();

    }

}

/*

* 连队0领取武器

0

1

2

3

4

5

6

7

8

9

连队0领取完毕

连队1领取武器

0

1

2

3

4

5

 

Process finished with exit code 0

 

* */

用volatile设置boolean标记位

  • 下面这段代码,通过改变标志位的值会成功终止线程

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

public class Volatile implements Runnable {

    private volatile boolean canceled = false;

    @Override

    public void run() {

        int num = 0;

        try {

            while (num <= 10000 && !canceled) {

                if (num % 100 == 0) {

                    System.out.println(num + " 是100的倍数");

                }

                num++;

                Thread.sleep(1);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

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

        Volatile v = new Volatile();

        Thread thread = new Thread(v);

        thread.start();

        Thread.sleep(1000);

        v.canceled = true;

    }

}

/*

*0 是100的倍数

100 是100的倍数

200 是100的倍数

300 是100的倍数

400 是100的倍数

500 是100的倍数

600 是100的倍数

Process finished with exit code 0

*

* */

  • 当陷入阻塞的时候,是无法停止线程的
  • 下面这段代码的运行结果,并没有打印生产者停止运行,说明根本没有执行生产者的finally那部分代码
  • 同时程序也没停止
  • 原因见生产者代码 while循环中的注释

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

// 模拟生产者和消费者

public class cantStop {

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

        // 阻塞队列

        // 满了之后,放不进去

        // 空的时候取数据,也会堵塞

        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);

        Producer producer = new Producer(storage);

        Thread producerThread = new Thread(producer);

        producerThread.start();

        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);

        while (consumer.needMore()) {

            System.out.println(consumer.storage.take() + "被消费");

            Thread.sleep(100);

        }

        System.out.println("消费者不需要更多数据");

        // 消费者不需要数据,让生产者停下来

        producer.canceled = true;

    }

}

// 生产者

class Producer implements Runnable {

    public volatile boolean canceled = false;

    BlockingQueue storage;

    public Producer(BlockingQueue storage) {

        this.storage = storage;

    }

    @Override

    public void run() {

        int num = 0;

        try {

            while (num <= 10000 && !canceled) {

                if (num % 100 == 0) {

                    // 当堵塞队列满了之后,会堵塞在这里,而这段代码没有判断机制

                    storage.put(num);

                    System.out.println("num" + "生产");

                }

                num++;

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

            System.out.println("生产者停止运行");

        }

    }

}

// 消费者

class Consumer {

    BlockingQueue storage;

    public Consumer(BlockingQueue storage) {

        this.storage = storage;

    }

    public boolean needMore() {

        if (Math.random() > 0.9) {

            return false;

        }

        return true;

    }

}

/*

* num生产

num生产

num生产

num生产

num生产

num生产

num生产

num生产

num生产

num生产

0被消费

num生产

消费者不需要更多数据

 

*

* */

  • 将上面代码用interrupt进行中断
  • 程序成功停止

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

public class finxed {

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

        finxed finxed = new finxed();

        // 阻塞队列

        // 满了之后,放不进去

        // 空的时候取数据,也会堵塞

        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);

        Producer producer = finxed.new Producer(storage);

        Thread producerThread = new Thread(producer);

        producerThread.start();

        Thread.sleep(1000);

        Consumer consumer = finxed.new Consumer(storage);

        while (consumer.needMore()) {

            System.out.println(consumer.storage.take() + "被消费");

            Thread.sleep(100);

        }

        System.out.println("消费者不需要更多数据");

        // 消费者不需要数据,让生产者停下来

        producerThread.interrupt();

    }

    class Producer implements Runnable {

        public volatile boolean canceled = false;

        BlockingQueue storage;

        public Producer(BlockingQueue storage) {

            this.storage = storage;

        }

        @Override

        public void run() {

            int num = 0;

            try {

                while (num <= 10000 && !Thread.currentThread().isInterrupted()) {

                    if (num % 100 == 0) {

                        storage.put(num);

                        System.out.println("num" + "生产");

                    }

                    num++;

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                System.out.println("生产者停止运行");

            }

        }

    }

    class Consumer {

        BlockingQueue storage;

        public Consumer(BlockingQueue storage) {

            this.storage = storage;

        }

        public boolean needMore() {

            if (Math.random() > 0.9) {

                return false;

            }

            return true;

        }

    }

}

/*

* 2100被消费

num生产

2200被消费

num生产

2300被消费

num生产

消费者不需要更多数据

生产者停止运行

java.lang.InterruptedException

   at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)

   at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)

   at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)

   at com.jx.JavaTest.stopThread.volatiledmo.finxed$Producer.run(finxed.java:51)

   at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

*

* */

interrupt源码查看

  • 这段代码做的都是一些判断,真正执行中断的代码时interrupt0
  • interrupt0是native代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public void interrupt() {

    if (this != Thread.currentThread())

        checkAccess();

    synchronized (blockerLock) {

        Interruptible b = blocker;

        if (b != null) {

            interrupt0();           // Just to set the interrupt flag

            b.interrupt(this);

            return;

        }

    }

    interrupt0();

}

private native void interrupt0();

interrupt相关函数练习

  • isInterrupted获取中断标志,获取的是前面的线程
  • interrupted获取中断标志并重置,只关心执行的线程,所以下面代码执行的是main线程

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public class InterruptedTest {

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

        Thread thread = new Thread(new Runnable() {

            @Override

            public void run() {

                while (true) {

 

                }

            }

        });

        thread.start();

        thread.interrupt();

        // 获取中断标志

        System.out.println(thread.isInterrupted()); // true

        // 获取中断标志并重置

        System.out.println(thread.interrupted()); //false

        System.out.println(Thread.interrupted()); // false

        System.out.println(thread.isInterrupted()); //true

        thread.join();

        System.out.println("over");

    }

}


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • intellij idea自动生成类注释和方法注释配置方法

    intellij idea自动生成类注释和方法注释配置方法
    intellij idea自动生成类注释和方法注释 1.新建类,自动生成类注释的模板配置 File-Settings-Editor-File and Code Templates-Class 1 2 3 4 5 /** @Description: T
  • Java线程的停止实现原理介绍
    线程停止的原理 使用interrupt来通知,而不是强制 java提供了interrrupt让一个线程来通知另一个线程停止 如果想中断一个线程,但是那个线程不
  • springboot按月分表的实现方式介绍

    springboot按月分表的实现方式介绍
    在实际工作中,会遇到业务比较集中的情况,随着时间推延,这部分业务关联的mysql表就会越来越大,十分臃肿。尽管在项目架构上做了读写
  • jmap执行失败如何获取heapdump

    jmap执行失败如何获取heapdump
    在之前的OOM问题复盘中,我们添加了jmap脚本来自动dump内存现场,方便排查OOM问题。 但当我反复模拟OOM场景测试时,发现jmap有时可以dump成功
  • Spring refresh()源码解析介绍

    Spring refresh()源码解析介绍
    正文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public void refresh() throws BeansException, IllegalStateExcept
  • 线程阻塞唤醒工具LockSupport使用介绍
    LockSupport 简介 LockSupport是 Java 并发编程中一个非常重要的组件,我们熟知的并发组件Lock、线程池、CountDownLatch等都是基于AQS实现的,而AQS内
  • RocketMQ的存储模型全介绍
    RocketMQ有Producer、Consumer、NameSrv、Broker四个部分。其中Broker用于存储消息,维护消息队列和订阅关系,是RocketMQ四个部分中最重要的一个部分
  • 规则引擎Drools的应用介绍
    现在有这么个需求,网上购物,需要根据不同的规则计算商品折扣,比如VIP客户增加5%的折扣,购买金额超过1000元的增加10%的折扣等,而且
  • Spring populateBean属性赋值和自动注入
    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mb
  • ThreadPoolExecutor参数的用法及说明
    一、ThreadPoolExecutor核心参数说明 1、corePoolSize:核心线程数 * 核心线程会一直存活,及时没有任务需要执行 * 当线程数小于核心线程数时,即
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计