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

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

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

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

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

java 复制代码
public class Box {
    public static ArrayList list1 = new ArrayList();
    public static ArrayList list2 = new ArrayList();
}
java 复制代码
public class MyThread extends Thread{

    @Override
    public void run() {
        try {
            while (true) {
                if (Thread.interrupted()) {
                    throw new InterruptedException("线程中断");
                }
                for (int i = 0; i < 10000; i++) {
                    new String("" + Math.random());
                }
                Box.list1.add("数据A");
                System.out.println("list1 size = " + Box.list1.size());
            }
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
        try {
            while (true) {
                if (Thread.interrupted()) {
                    throw new InterruptedException("线程中断");
                }
                for (int i = 0; i < 10000; i++) {
                    new String("" + Math.random());
                }
                Box.list2.add("数据B");
                System.out.println("list2 size = " + Box.list2.size());
            }
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        boolean list1Isinterrupt = false;
        boolean list2Isinterrupt = false;
        while(myThread.isAlive()){
            if(Box.list1.size() > 500 && list1Isinterrupt == false){
                myThread.interrupt();
                list1Isinterrupt = false;
            }
            if(Box.list1.size() > 600 && list1Isinterrupt == false){
                myThread.interrupt();
                list2Isinterrupt = false;
            }
            Thread.sleep(100);

        }
    }
}

3.4 能停止的线程-异常法

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

【示例3.4.1】

java 复制代码
public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 500000; i++) {
            if(MyThread.interrupted()){
                System.out.println("这已经是停止状态了,我要退出了");
                break;
            }
            System.out.println("i = " + (i+1));
        }
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        try {
            MyThread myThread = new MyThread();
            myThread.start();
            Thread.sleep(100);
            myThread.interrupt();
        }catch (InterruptedException e){
            System.out.println("main catch");
            e.printStackTrace();
        }
    }
}

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

【示例】

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

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

java 复制代码
public class MyThread1 extends Thread{
    @Override
    public void run(){
        super.run();
        try{
            for (int i = 0; i < 500000; i++) {
                if(MyThread1.interrupted()){
                    System.out.println("这里是停止状态,退出!");
                    throw  new InterruptedException();
                }
                System.out.println("i = " + (i + 1));
            }
        }catch (InterruptedException e){
            System.out.println("MyThread1 线程 被catch了");
            e.printStackTrace();
        }
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        try {
            MyThread1 myThread = new MyThread1();
            myThread.start();
            Thread.sleep(100);
            myThread.interrupt();
        }catch (InterruptedException e){
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

4 暂停线程

4.1 使用suspend()暂停线程

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

【示例4.1】

java 复制代码
public class MyThread1 extends Thread{
    private long i = 0;

    public long getI() {
        return i;
    }

    public void setI(long i) {
        this.i = i;
    }

    @Override
    public void run(){
        while(true){
            i++;
        }
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        try{
            MyThread1 thread = new MyThread1();
            thread.start();
            Thread.sleep(5000);
            //A段
            thread.suspend();
            System.out.println("A = " + System.currentTimeMillis() + " i ="+ thread.getI());
            Thread.sleep(5000);
            System.out.println("A = " + System.currentTimeMillis() + " i ="+ thread.getI());
            //B段
            thread.suspend();
            Thread.sleep(5000);
            //C段
            thread.suspend();
            System.out.println("B = " + System.currentTimeMillis() + " i ="+ thread.getI());
            Thread.sleep(5000);
            System.out.println("B = " + System.currentTimeMillis() + " i ="+ thread.getI());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

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

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

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

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

【示例4.2】

java 复制代码
public class SynchronzedObject {
    synchronized public void printString(){
        System.out.println("begin");
        if(Thread.currentThread().getName() .equals("a")){
            System.out.println("a 线程永远suspend");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        try {
            final  SynchronzedObject object = new SynchronzedObject();
            Thread t1 = new Thread(){
                @Override
                public void run(){
                    object.printString();

                }
            };
            t1.setName("a");
            t1.start();
            Thread.sleep(1000);

            Thread t2 = new Thread(){
                @Override
                public void run(){
                    System.out.println("t2启动了,但进入不了printString方法");
                    System.out.println("因为被t1锁定并永远suspend了");
                    object.printString();
                }
            };
            t2.start();

        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

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

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

【示例4.3.1】

java 复制代码
public class MyThread1 extends Thread{
    @Override
    public void run(){
        System.out.println("begin" + System.currentTimeMillis()/1000);
        LockSupport.park();
        System.out.println("end" + System.currentTimeMillis()/1000);
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread1 t1 = new MyThread1();
        t1.start();
        Thread.sleep(1000);
        LockSupport.unpark(t1);


    }
}

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

4.4 yield()方法

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

【4.4.1】

java 复制代码
public class MyThread1 extends Thread{
    @Override
    public void run(){

        Long begin = System.currentTimeMillis() ;
        int count = 0;
        for (int i = 0; i < 5000000; i++) {
            //Thread.yield();
            count = count + (i + 1);

        }
        Long end = System.currentTimeMillis() ;
        System.out.println("用时:"+(end - begin) + "毫秒");
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.start();
    }
}

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

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

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

5 线程的优先级

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

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

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

java 复制代码
public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

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

复制代码
throw new IllegalArgumentException()。

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

java 复制代码
    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

5.1 线程优先级的继承特性

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

【示例5.1.1】

java 复制代码
public class MyThread2 extends Thread{
    @Override
    public void run(){
        System.out.println("MyThread2 的优先级 = " + this.getPriority());
    }
}
java 复制代码
public class MyThread1 extends Thread{
    @Override
    public void run(){
        System.out.println("MyThread1 的优先级 = " + this.getPriority());
        MyThread2 t2 = new MyThread2();
        t2.start();
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        System.out.println("开始时main线程的优先级 = " + Thread.currentThread().getPriority());
        //Thread.currentThread().setPriority(8);
        System.out.println("结束时main线程的优先级 = " + Thread.currentThread().getPriority());
        MyThread1 t1 = new MyThread1();
        t1.start();
    }
}

运行结果是

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

5.2 线程优先级的规律性

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

【示例5.2.1】

java 复制代码
public class MyThread3 extends Thread{
    @Override
    public void run(){
        long begin = System.currentTimeMillis();
        long addResult = 0L;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 100000; j++) {
                Random random = new Random();
                random.nextInt();
                addResult = addResult + i;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("t1 消耗时间 = " + (end - begin) + "毫秒");
    }

}
java 复制代码
public class MyThread4 extends Thread{
    @Override
    public void run(){
        long begin = System.currentTimeMillis();
        long addResult = 0L;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 100000; j++) {
                Random random = new Random();
                random.nextInt();
                addResult = addResult + i;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("t2 消耗时间 = " + (end - begin) + "毫秒");
    }
}
java 复制代码
public class Run2 {
    public static void main(String[] args) {

        for (int i = 0; i < 5; i++) {
            MyThread3 t1 = new MyThread3();
            t1.setPriority(1);
            t1.start();
            MyThread4 t2 = new MyThread4();
            t2.setPriority(10);
            t2.start();

        }

    }
}

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

5.3 守护线程

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

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

【示例5.3.1】

java 复制代码
public class MyThread extends Thread{
    private  int i = 0;
    @Override
    public void run(){
        try {
           while (true){
               i ++;
               System.out.println("i = " + i);
               Thread.sleep(1000);
           }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
java 复制代码
public class Run1 {
    public static void main(String[] args) {
        try {
            MyThread t1 = new MyThread();
            t1.setDaemon(true);
            t1.start();
            Thread.sleep(5000);
            System.out.println("我离开了t1对象也不再打印了吗,就是停止了");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

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

相关推荐
考虑考虑14 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613515 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊16 小时前
Java学习第22天 - 云原生与容器化
java
渣哥17 小时前
原来 Java 里线程安全集合有这么多种
java
间彧17 小时前
Spring Boot集成Spring Security完整指南
java
间彧18 小时前
Spring Secutiy基本原理及工作流程
java
Java水解19 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆21 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学21 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole1 天前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端