• Java多线程核心技术第一阶段-Java多线程基础 02


    接上篇:Java多线程核心技术第一阶段-Java多线程基础 01

    3.3 清除中断状态的使用场景

            this.interrupted()方法具有清除状态标志值的功能,借用此特性可以实现一些效果。

    【示例3.3.1】在MyThread4线程中向list1和list2存放数据,基于单一职责原则,MyThread4线程只负责存放数据,不负责处理存放的数据量,数据量由main线程进行处理。

    1. public class Box {
    2. public static ArrayList list1 = new ArrayList();
    3. public static ArrayList list2 = new ArrayList();
    4. }
    1. public class MyThread extends Thread{
    2. @Override
    3. public void run() {
    4. try {
    5. while (true) {
    6. if (Thread.interrupted()) {
    7. throw new InterruptedException("线程中断");
    8. }
    9. for (int i = 0; i < 10000; i++) {
    10. new String("" + Math.random());
    11. }
    12. Box.list1.add("数据A");
    13. System.out.println("list1 size = " + Box.list1.size());
    14. }
    15. } catch (InterruptedException exception) {
    16. exception.printStackTrace();
    17. }
    18. try {
    19. while (true) {
    20. if (Thread.interrupted()) {
    21. throw new InterruptedException("线程中断");
    22. }
    23. for (int i = 0; i < 10000; i++) {
    24. new String("" + Math.random());
    25. }
    26. Box.list2.add("数据B");
    27. System.out.println("list2 size = " + Box.list2.size());
    28. }
    29. } catch (InterruptedException exception) {
    30. exception.printStackTrace();
    31. }
    32. }
    33. }
    1. public class Run1 {
    2. public static void main(String[] args) throws InterruptedException {
    3. MyThread myThread = new MyThread();
    4. myThread.start();
    5. boolean list1Isinterrupt = false;
    6. boolean list2Isinterrupt = false;
    7. while(myThread.isAlive()){
    8. if(Box.list1.size() > 500 && list1Isinterrupt == false){
    9. myThread.interrupt();
    10. list1Isinterrupt = false;
    11. }
    12. if(Box.list1.size() > 600 && list1Isinterrupt == false){
    13. myThread.interrupt();
    14. list2Isinterrupt = false;
    15. }
    16. Thread.sleep(100);
    17. }
    18. }
    19. }

    3.4 能停止的线程-异常法

            根据前面的介绍,只需要通过线程中的for语句来判断线程是否是停止状态即可判断后面的代码是否可运行,如果是停止状态,则后面的代码不在运行。

    【示例3.4.1】

    1. public class MyThread extends Thread{
    2. @Override
    3. public void run(){
    4. for (int i = 0; i < 500000; i++) {
    5. if(MyThread.interrupted()){
    6. System.out.println("这已经是停止状态了,我要退出了");
    7. break;
    8. }
    9. System.out.println("i = " + (i+1));
    10. }
    11. }
    12. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. try {
    4. MyThread myThread = new MyThread();
    5. myThread.start();
    6. Thread.sleep(100);
    7. myThread.interrupt();
    8. }catch (InterruptedException e){
    9. System.out.println("main catch");
    10. e.printStackTrace();
    11. }
    12. }
    13. }

            上面的示例中,虽然停止了线程,但是如果for语句下面还有语句,那么程序还会继续执行。

    【示例】 

    1. public class MyThread1 extends Thread{
    2. @Override
    3. public void run(){
    4. for (int i = 0; i < 500000; i++) {
    5. if(MyThread1.interrupted()){
    6. System.out.println("这已经是停止状态了,我要退出了");
    7. break;
    8. }
    9. System.out.println("i = " + (i+1));
    10. }
    11. System.out.println("此行被输出,如果此行代码时for又继续执行,线程并未停止");
    12. }
    13. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. try {
    4. MyThread1 myThread = new MyThread1();
    5. myThread.start();
    6. Thread.sleep(100);
    7. myThread.interrupt();
    8. }catch (InterruptedException e){
    9. System.out.println("main catch");
    10. e.printStackTrace();
    11. }
    12. }
    13. }

    如何解决语句继续运行的问题呢?看一下更新后的代码:

    1. public class MyThread1 extends Thread{
    2. @Override
    3. public void run(){
    4. super.run();
    5. try{
    6. for (int i = 0; i < 500000; i++) {
    7. if(MyThread1.interrupted()){
    8. System.out.println("这里是停止状态,退出!");
    9. throw new InterruptedException();
    10. }
    11. System.out.println("i = " + (i + 1));
    12. }
    13. }catch (InterruptedException e){
    14. System.out.println("MyThread1 线程 被catch了");
    15. e.printStackTrace();
    16. }
    17. }
    18. }

    1. public class Run1 {
    2. public static void main(String[] args) {
    3. try {
    4. MyThread1 myThread = new MyThread1();
    5. myThread.start();
    6. Thread.sleep(100);
    7. myThread.interrupt();
    8. }catch (InterruptedException e){
    9. System.out.println("main catch");
    10. e.printStackTrace();
    11. }
    12. System.out.println("end!");
    13. }
    14. }

    4 暂停线程

    4.1 使用suspend()暂停线程

            暂停线程意味着此线程还可以恢复运行,在Java多线程中可以使用suspend()方法暂停线程,使用resume()方法来恢复线程。

    【示例4.1】 

    1. public class MyThread1 extends Thread{
    2. private long i = 0;
    3. public long getI() {
    4. return i;
    5. }
    6. public void setI(long i) {
    7. this.i = i;
    8. }
    9. @Override
    10. public void run(){
    11. while(true){
    12. i++;
    13. }
    14. }
    15. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. try{
    4. MyThread1 thread = new MyThread1();
    5. thread.start();
    6. Thread.sleep(5000);
    7. //A段
    8. thread.suspend();
    9. System.out.println("A = " + System.currentTimeMillis() + " i ="+ thread.getI());
    10. Thread.sleep(5000);
    11. System.out.println("A = " + System.currentTimeMillis() + " i ="+ thread.getI());
    12. //B段
    13. thread.suspend();
    14. Thread.sleep(5000);
    15. //C段
    16. thread.suspend();
    17. System.out.println("B = " + System.currentTimeMillis() + " i ="+ thread.getI());
    18. Thread.sleep(5000);
    19. System.out.println("B = " + System.currentTimeMillis() + " i ="+ thread.getI());
    20. }catch (InterruptedException e){
    21. e.printStackTrace();
    22. }
    23. }
    24. }

            stop()方法用户销毁线程对象,如果想继续运行线程,则必须使用start()重新启动线程,而suspend()方法用于让线程不再执行任务,线程对象并不销毁,只在当前所执行的代码处暂停,未来还可以恢复运行。

            从控制台输出的时间上来看,线程的确被暂停了,而且还可以恢复成运行状态。 

    4.2 suspend()和resume()的缺点-独占

            如果suspend()和resume()方法使用不当,极易造成公共同步对象被独占,其他线程无法访问公共同步对象的结果。

    【示例4.2】

    1. public class SynchronzedObject {
    2. synchronized public void printString(){
    3. System.out.println("begin");
    4. if(Thread.currentThread().getName() .equals("a")){
    5. System.out.println("a 线程永远suspend");
    6. Thread.currentThread().suspend();
    7. }
    8. System.out.println("end");
    9. }
    10. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. try {
    4. final SynchronzedObject object = new SynchronzedObject();
    5. Thread t1 = new Thread(){
    6. @Override
    7. public void run(){
    8. object.printString();
    9. }
    10. };
    11. t1.setName("a");
    12. t1.start();
    13. Thread.sleep(1000);
    14. Thread t2 = new Thread(){
    15. @Override
    16. public void run(){
    17. System.out.println("t2启动了,但进入不了printString方法");
    18. System.out.println("因为被t1锁定并永远suspend了");
    19. object.printString();
    20. }
    21. };
    22. t2.start();
    23. }catch (InterruptedException e){
    24. e.printStackTrace();
    25. }
    26. }
    27. }

    4.3 使用LockSupport实现线程暂停与恢复

            suspend()和resume()方法时过期作废的,如果想实现同样的功能,可以使用JDK并发包里提供的LockSupport类作为替代,效果是一样的。

    【示例4.3.1】 

    1. public class MyThread1 extends Thread{
    2. @Override
    3. public void run(){
    4. System.out.println("begin" + System.currentTimeMillis()/1000);
    5. LockSupport.park();
    6. System.out.println("end" + System.currentTimeMillis()/1000);
    7. }
    8. }
    1. public class Run1 {
    2. public static void main(String[] args) throws InterruptedException {
    3. MyThread1 t1 = new MyThread1();
    4. t1.start();
    5. Thread.sleep(1000);
    6. LockSupport.unpark(t1);
    7. }
    8. }

            park()方法的作用是将线程暂停,unpark()方法的作用是恢复线程的运行。如果先执行unpark在执行park方法,则park方法不会呈现暂停的效果。

    4.4 yield()方法

            yield方法的作用是放弃当前的CPU资源,让其他任务去占用CPU执行时间,放弃的时间不确定,有可能刚刚放弃,马上有获取CPU时间片。

    【4.4.1】

    1. public class MyThread1 extends Thread{
    2. @Override
    3. public void run(){
    4. Long begin = System.currentTimeMillis() ;
    5. int count = 0;
    6. for (int i = 0; i < 5000000; i++) {
    7. //Thread.yield();
    8. count = count + (i + 1);
    9. }
    10. Long end = System.currentTimeMillis() ;
    11. System.out.println("用时:"+(end - begin) + "毫秒");
    12. }
    13. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. MyThread1 t1 = new MyThread1();
    4. t1.start();
    5. }
    6. }

    第一次运行时,Thread.yield()方法注释,运行结果:

    第二次运行时,Thread.yield()方法放开,运行结果:

    第二次将CPU资源让给其他资源,导致速度变慢。

    5 线程的优先级

            在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,即CPU优先执行优先级较高的线程对象中的任务,其实就是让优先级高的线程获取更多的PCU时间片。

            设置线程优先级有助于“线程规划器”确定在下一次选择哪一个线程来优先执行。

            使用setPriority()方法设置线程的优先级,此方法在JDK的源码如下:

    1. public final void setPriority(int newPriority) {
    2. ThreadGroup g;
    3. checkAccess();
    4. if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
    5. throw new IllegalArgumentException();
    6. }
    7. if((g = getThreadGroup()) != null) {
    8. if (newPriority > g.getMaxPriority()) {
    9. newPriority = g.getMaxPriority();
    10. }
    11. setPriority0(priority = newPriority);
    12. }
    13. }

            在Java中线程的优先级分为10个等级,即1~10,如果小于1或大于10,则抛出

    throw new IllegalArgumentException()。

            JDK使用三个常量来预定于优先级的值,代码如下:

    1. /**
    2. * The minimum priority that a thread can have.
    3. */
    4. public final static int MIN_PRIORITY = 1;
    5. /**
    6. * The default priority that is assigned to a thread.
    7. */
    8. public final static int NORM_PRIORITY = 5;
    9. /**
    10. * The maximum priority that a thread can have.
    11. */
    12. public final static int MAX_PRIORITY = 10;

    5.1 线程优先级的继承特性

            在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

    【示例5.1.1】

    1. public class MyThread2 extends Thread{
    2. @Override
    3. public void run(){
    4. System.out.println("MyThread2 的优先级 = " + this.getPriority());
    5. }
    6. }
    1. public class MyThread1 extends Thread{
    2. @Override
    3. public void run(){
    4. System.out.println("MyThread1 的优先级 = " + this.getPriority());
    5. MyThread2 t2 = new MyThread2();
    6. t2.start();
    7. }
    8. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. System.out.println("开始时main线程的优先级 = " + Thread.currentThread().getPriority());
    4. //Thread.currentThread().setPriority(8);
    5. System.out.println("结束时main线程的优先级 = " + Thread.currentThread().getPriority());
    6. MyThread1 t1 = new MyThread1();
    7. t1.start();
    8. }
    9. }

    运行结果是

    此时把Run1类的注释放开后,运行结果是

    5.2 线程优先级的规律性

            虽然使用setPriority()方法可以设置线程的优先级,但还是没有看到设置优先级带来的效果。

    【示例5.2.1】

    1. public class MyThread3 extends Thread{
    2. @Override
    3. public void run(){
    4. long begin = System.currentTimeMillis();
    5. long addResult = 0L;
    6. for (int i = 0; i < 10; i++) {
    7. for (int j = 0; j < 100000; j++) {
    8. Random random = new Random();
    9. random.nextInt();
    10. addResult = addResult + i;
    11. }
    12. }
    13. long end = System.currentTimeMillis();
    14. System.out.println("t1 消耗时间 = " + (end - begin) + "毫秒");
    15. }
    16. }
    1. public class MyThread4 extends Thread{
    2. @Override
    3. public void run(){
    4. long begin = System.currentTimeMillis();
    5. long addResult = 0L;
    6. for (int i = 0; i < 10; i++) {
    7. for (int j = 0; j < 100000; j++) {
    8. Random random = new Random();
    9. random.nextInt();
    10. addResult = addResult + i;
    11. }
    12. }
    13. long end = System.currentTimeMillis();
    14. System.out.println("t2 消耗时间 = " + (end - begin) + "毫秒");
    15. }
    16. }
    1. public class Run2 {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 5; i++) {
    4. MyThread3 t1 = new MyThread3();
    5. t1.setPriority(1);
    6. t1.start();
    7. MyThread4 t2 = new MyThread4();
    8. t2.setPriority(10);
    9. t2.start();
    10. }
    11. }
    12. }

            高优先级的线程总是大部分先执行王城,但不代表高优先级的线程全部执行完成。另外,并不是t1线程先被main线程调用就先执行完。当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关。

    5.3 守护线程

            Java中有两种线程,一种是用户线程(非守护线程),另一种是守护线程。

            守护线程是一种特殊的线程,当进程中不存在非守护时,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程,则垃圾会后线程也没有存在的必要,会自动销毁。主线程main在本篇中属于用户线程,凡是调用setDaemon(true)方法并且参数为true时的线程才是守护线程。

    【示例5.3.1】

    1. public class MyThread extends Thread{
    2. private int i = 0;
    3. @Override
    4. public void run(){
    5. try {
    6. while (true){
    7. i ++;
    8. System.out.println("i = " + i);
    9. Thread.sleep(1000);
    10. }
    11. }catch (InterruptedException e){
    12. e.printStackTrace();
    13. }
    14. }
    15. }
    1. public class Run1 {
    2. public static void main(String[] args) {
    3. try {
    4. MyThread t1 = new MyThread();
    5. t1.setDaemon(true);
    6. t1.start();
    7. Thread.sleep(5000);
    8. System.out.println("我离开了t1对象也不再打印了吗,就是停止了");
    9. }catch (InterruptedException e){
    10. e.printStackTrace();
    11. }
    12. }
    13. }

    【注意】要在执行start方法前先执行setDaemon(boolean)方法,不然会出现异常。 

  • 相关阅读:
    Appium入门自动化测试(6)—— Appium 常用方法的自己动手封装
    Python:如何在一个月内学会爬取大规模数据
    YZ系列工具之YZ13:VBA_过滤数据并行删除
    群晖NAS教程(二十五)、利用web station安装nextcloud
    复杂度分析
    2. 安装运行SQL语句的环境
    计算机毕业设计之java+javaweb的课堂教学效果实时评价系统
    Python爬取阿里巴巴商城数据
    javaIO流06:节点流和处理流(包装流)(以代码的方式简单理解处理流的装饰者模式)
    嵌入式工程师面试题
  • 原文地址:https://blog.csdn.net/geminigoth/article/details/134396500