关于java:Java多线程入门

2次阅读

共计 16923 个字符,预计需要花费 43 分钟才能阅读完成。

我是阿福,公众号「<font color=’red’>JavaClub</font>」作者,一个在后端技术路上摸盘滚打的程序员,在进阶的路上,共勉!

文章已收录在 JavaSharing 中,蕴含 Java 技术文章,面试指南,资源分享。

Java 多线程入门

文章次要波及线程的启动,如何使多线程暂停,如何使多线程进行,线程的优先级级线程平安相干的问题。

1.1 过程和多线程的概念及线程的长处

过程:过程是操作系统构造的根底,是一次程序的执行,是一个程序及其数据在处理机上程序执行时所产生的流动,是程序在一个数据汇合上运行的过程,它是零碎进行资源分配和调度的独立单位。(起源:百度百科)

看了这段话是不是非常形象,不好了解,然而如果你看到图所示的内容,你还对过程不了解吗?NO, NO, NO

难道一个正在操作系统中运行的 exe 程序 能够了解成一个过程吗?没错!是他!是他!就是他!

那么在 Windows 工作管理器 列表中,齐全能够将运行在内存中的 exe 文件了解成过程,过程是受操作系统治理的根本运行单元

那什么是线程呢?

线程能够了解成是过程中独立运行的子工作,比方 QQ.exe 运行时有好多子工作在同时运行。比方:视频聊天线程,下载文件线程,传输数据线程等。这些不同的工作齐全能够同时在运行,其中每一项工作能够了解成不同的线程在工作。

那么这样的长处是什么呢?例如咱们应用多任务操作系统 Windows 后,能够最大限度地利用 CPU 的闲暇工夫来解决其余的工作,比方一边让操作系统解决正在由打印机打印的数据,一边应用 Word 编辑文档,而 CPU 在这些工作之间不停地切换,因为速度十分快,给读者的感触就是这些工作仿佛在同时运行。

为了更好了解多线程的劣势,首先咱们通过单利模型图来了解一下单任务的毛病。

工作 1 和工作 2 是两个齐全独立,互不相干的工作,工作一是在期待近程服务器返回数据,以便进行前期的解决,这是 CPU 始终处于期待状态,始终在空运行。而工作 2 始终处于期待状态必须等工作 1 返回数据能力运行,这样零碎的运行效率大幅升高。单任务的特点就是 排队执行,也就是同步,就像在 cmd 中输出一条命令后,必须期待这条命令执行完才能够执行下一条命令一样。所以单线程的毛病是:CPU 利用率大幅升高

从图中咱们能够发现,CPU 齐全能够在工作 1 和工作 2 之间来回切换,使工作 2 不用期待 10 秒后再运行,零碎的运行效率大大失去晋升。

留神一点!!!!!

多线程是异步的,千万不要把 IDEA 里代码的程序当成线程执行的程序,线程被调用时随机的。

1.2 应用多线程

1.2.1 继承 Thread 类

在 Java 的 JDK 开发包中,曾经自带了对多线程的反对,实现多线程编程的形式次要有两种:一种是继承 Thread 类,一种是实现 Runable 接口。

创立多线程之前咱们先看看 Thread 的构造,如下:

public class Thread implements Runnable 

从下面的源代码中咱们能够发现,Thread类实现了 Runnable 接口,他们之间具备多态关系。

其实,应用继承 Thread 类的形式实现多线程时,最大的局限性就是不反对多继承,因为在 Java 语言的特点就是 单继承 ,所以为了实现多继承齐全能够采纳实现Runnable 接口的形式。总的来说,没有什么实质的区别。

首先咱们创立一个自定义的线程呢类 MyThread.java,继承 Thread 类并且重写 run()办法,代码如下:

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

运行类代码如下:

public class Test {public static void main(String[] args) {MyThread myThread=new MyThread();
        myThread.start();
        System.out.println("运行完结");
    }
}

运行后果如下:

运行完结
MyThread

从运行的后果来看,MyThread.java中的 run 办法执行的工夫比拟晚,这也阐明 在应用多线程技术时,代码的运行后果与代码执行程序或调用程序是无关的。

为什么会呈现这样的后果呢?是因为线程是一个子工作,CPU 以不确定的形式或是以随机的工夫来调用线程中的 run 办法,所以会呈现先打印“运行完结”,后输入“MyThread”这样的后果。

下面咱们提出线程调用的随机性,上面咱们创立MyThread.java 来演示线程的随机性。

创立自定义线程类MyThread.java,代码如下:

public class MyThread extends Thread {
    @Override
    public void run() {
        try {for (int i = 0; i < 5; i++) {int time = (int) Math.random() * 1000;
                Thread.sleep(time);
                System.out.println("run=" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {e.printStackTrace();
        }

    }
}

再创立运行类Test.java, 代码如下:

public class Test {public static void main(String[] args) {MyThread myThread = new MyThread();
        myThread.setName("myThread");
        myThread.start();
        try {for (int i = 0; i < 5; i++) {int time = (int) Math.random() * 1000;
                Thread.sleep(time);
                System.out.println("main=" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {e.printStackTrace();
        }

    }
}

代码运行后果:

main=main
run=myThread
main=main
run=myThread
main=main
run=myThread
main=main
run=myThread
main=main
run=myThread

在代码中,为了展现线程具备随机性,所以应用随机数的模式来使线程失去挂起的成果,从而体现出 CPU 执行那个线程具备不确定性。

阐明:MyThread.java中的 start()办法告诉“线程布局器 ”,此线程曾经准别就绪,期待调用线程对象的 run 办法,这个过程其实就是让 CPU 安顿一个工夫来调用MyThread.java 类中的 run 办法,也就是使线程失去运行。

在强调一点,执行 start()办法的程序不代表线程启动的程序,创立测试类阐明一下,代码如下:

public class MyThread extends Thread {
    private int i;

    public MyThread(int i) {this.i = i;}

    @Override
    public void run() {System.out.println("i=" + i);

    }
}

运行类Test.java, 代码如下:

public class Test {public static void main(String[] args) {MyThread myThread1 = new MyThread(1);
        MyThread myThread2 = new MyThread(2);
        MyThread myThread3 = new MyThread(3);
        MyThread myThread4 = new MyThread(4);
        MyThread myThread5 = new MyThread(5);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();}

程序运行后的后果如图:

i=1
i=2
i=5
i=3
i=4

1.2.2 实现 Runnable 接口

如果咱们创立的线程类曾经有一个父类了,这时候就不能继承 Thread 类了,因为在 Java 中不反对多继承,所以咱们须要实现 Runnable 接口来实现多线程。

创立一个实现 Runnable 接口的类

public class MyRunnable implements Runnable{

    @Override
    public void run() {System.out.println("运行中!!!!!");
    }
}

如何应用 MyRunnable.java 类呢,咱们看一下 Thread.java 类的构造函数,如下图所示:

Thread.java 类中的 8 个构造函数中,有两个构造函数 Thread(Runnable target) 和Thread(Runnable target, String name)能够传递 Runnable 接口,阐明构造函数反对传入一个 Runnable 接口的对象,运行类代码如下:

public class Test {public static void main(String[] args) {MyRunnable myRunnable=new MyRunnable();
       Thread thread=new Thread(myRunnable);
       thread.start();
        System.out.println("运行完结!!!");
    }
}

运行后果如图:

运行完结!!!
运行中!!!!!

另外须要阐明一点,Thread.java类也是实现 Runnable 接口,如下:

public class Thread implements Runnable 

那也就意味着构造函数 Thread(Runnable target) 不光能够传入 Runnable 接口对象,还能够传入一个 Thread.java 类的对象,这样做齐全能够将一个 Thread.java 对象的 run()办法交给其余的线程进行调用。

1.2.3 实例变量与线程平安

自定义线程类中的实例变量针对其余线程能够有 共享和不共享 之分,上面咱们离开来阐明这两点:

不共享数据的状况

不共享数据的状况如下图展现阐明:

上面咱们通过一个示例来看下数据不共享状况,创立一个 MyThread.java 类代码如下:

public class MyThread extends Thread {
    private int count = 5;

    public MyThread(String name) {this.setName(name);
    }

    @Override
    public void run() {while (count > 0) {
            count--;
            System.out.println("由" + this.currentThread().getName() + "" +" 计算, count " + count);
        }
    }
}

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) {MyThread myThread=new MyThread("A");
        MyThread myThread1=new MyThread("B");
        MyThread myThread2=new MyThread("C");
        myThread.start();
        myThread1.start();
        myThread2.start();}
}

不共享数据运行后果如下:

由 B 计算, count 4
由 C 计算, count 4
由 A 计算, count 4
由 C 计算, count 3
由 B 计算, count 3
由 C 计算, count 2
由 A 计算, count 3
由 C 计算, count 1
由 B 计算, count 2
由 C 计算, count 0
由 A 计算, count 2
由 B 计算, count 1
由 A 计算, count 1
由 B 计算, count 0
由 A 计算, count 0

咱们总共创立了 3 个线程,每个线程都有各自的 count 变量,本人缩小本人的 count 变量的值,这样的状况就是变量不共享。

那么,如果想实现 3 个线程独特对一个 count 变量进行减法操作的目标,该如何设计呢?

共享数据的状况

共享数据的状况如下图:

共享数据的状况就是多个线程同时拜访一个变量,比方实现投票性能的软件时,多个线程能够同时解决同一个人的票数。

上面咱们通过代码实例演示一下共享数据的状况,创立一个 MyThread.java 类代码如下:

public class MyThread extends Thread {
    private int count = 3;

    @Override
    public void run() {
        count--;
        System.out.println("由" + this.currentThread().getName() + "" +" 计算, count " + count);

    }
}

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) {MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread,"A");
        Thread thread1=new Thread(myThread,"B");
        Thread thread2=new Thread(myThread,"C");
        thread.start();
        thread1.start();
        thread2.start();}
}

运行后果:

由 A 计算, count 0
由 C 计算, count 0
由 B 计算, count 0

从运行后果咱们能够看出,线程 A,线程 B,线程 C 的 count 值都是 2,阐明 A,B,C 同时对 count 进行解决,产生了“非线程平安”的问题。

那咱们批改代码如下 即在 run() 办法后面加上 synchronized 关键字:

public synchronized void run() {
    count--;
    System.out.println("由" + this.currentThread().getName() + "" +" 计算, count " + count);
}

从新运行程序就不回产生值一样的状况了,结果显示如下:

由 A 计算, count 2
由 C 计算, count 1
由 B 计算, count 0

​ 通过在 run()办法后面加上 synchronized 关键字,使多个线程在执行 run()办法时,以排队的模式进行解决。但一个线程调用 run 前,先判断 run 办法有没有被上锁,如果上锁,阐明有其余线程正在调用 run 办法,必须等其余线程对 run 办法调用完结后才能够执行 run 办法。这样排队调用 run 办法的目标,也就达到按程序对 count 变量减 1 的成果了。同时 synchronized 能够在任何办法上加锁,而加锁的这段代码叫做“互斥区 ”或“ 临界区”。

另外阐明,当一个线程想要执行同步办法外面的代码时,线程首先去拿这把锁,如果可能拿到这把锁,那么这个线程就能够执行 synchronized 如果拿不到这把锁,那么线程就一直的尝试拿这把锁,直到可能拿到为止。

这里咱们引出一个概念,“非线程平安 ”。非线程平安次要是指 多个线程对同一个对象中的同一个实例变量进行操作时会呈现值被更改,值不同步的状况,进而影响程序的执行流程。

上面咱们来实现一个非线程平安的实例,loginServlet.java代码如下:

public class loginServlet {
    private static String usernameRef;
    private static String passwordRef;

    public static void doPost(String username, String password) {
        try {
            usernameRef = username;
            if (username.equals("username=" + username)) {Thread.sleep(5000);
            }
            passwordRef = password;
            System.out.println("username=" + usernameRef + "" +" password=" + passwordRef);
        } catch (InterruptedException e) {e.printStackTrace();
        }

    }
}

线程 ALogin.java 代码如下:

public class ALogin extends Thread{
    @Override
    public void run() {loginServlet.doPost("a","aa");
    }
}

线程 BLogin.java 代码如下:

public class BLogin extends Thread{
    @Override
    public void run() {loginServlet.doPost("b","bb");
    }
}

代码实例运行后果:

username=a password=bb
username=b password=bb

由运行后果咱们能够看出,呈现了线程不平安的问题,解决这个问题的办法咱们应用 synchronized 关键字润饰别调用的办法即可。

public static synchronized void doPost(String username, String password)

1.3 多线程罕用办法

1.3.1 CurrentThread() 办法

CurrentThread() 办法返回 代码段正在被哪个线程调用的信息,上面通过一个示例来阐明:

public class Test {public static void main(String[] args) {System.out.println(Thread.currentThread().getName());
    }
}

运行后果:

main

后果阐明,main 办法被名为 main 的线程调用。

持续试验,创立 MyThread.java代码如下:

public class MyThread extends Thread {public MyThread() {System.out.println("构造方法的打印:"+Thread.currentThread().getName());
        this.setName("MyThread");
    }

    @Override
    public void run() {System.out.println("run 办法的打印:"+Thread.currentThread().getName());
    }
}

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) {MyThread myThread=new MyThread();
        myThread.start();}
}

运行后果:

构造方法的打印:main
run 办法的打印:MyThread

从运行后果咱们能够发现,MyThread.java类的构造函数是被 main 线程调用的,而 run 办法是被名称为 MyThread 的线程调用的。

1.3.2 isAlive()办法

办法 isAlive()的性能是 判断以后的线程是否处于活动状态

创立 MyThread.java代码如下:

public class MyThread extends Thread {

    @Override
    public void run() {System.out.println("run="+this.isAlive());
    }
}

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) {MyThread myThread=new MyThread();
        System.out.println("begin==" + myThread.isAlive());
        myThread.start();
        // Thread.sleep(1000);
        System.out.println("end==" + myThread.isAlive());
    }
}

运行后果:

begin==false
end==true
run=true

办法 isAlive()的作用是测试线程是否处于活动状态,那什么是活动状态呢?活动状态就是线程曾经启动且尚未终止。即线程处于正在运行或筹备开始运行的状态,就认为线程是存活的。

阐明一下,如下代码:

System.out.println("end==" + myThread.isAlive());

尽管下面打印的值是 true, 然而此值是不确定的,打印 true 是因为 MyThread 线程还未执行结束,所以输入 true, 如果批改代码把 Test.java 中 Thread.sleep(1000)代码放开,运行后果输入是 false, 因为 MyThread 线程在 1 秒内就执行结束。

另外,在应用 isAlive()办法时,如果将线程对象以构造函数的形式传递给 Thread 对象进行 start()启动时,运行的后果和后面的实例是有差别的,造成这样差别的起因是来自于 Thread.currentThread()和 this 的差别。

1.3.3 sleep() 办法

办法 sleep()的作用是在指定的毫秒数内让以后“正在执行的线程 ”休眠()暂停执行,这个正在执行的线程是指 this.currentThread() 返回的线程,后面的实例也提到过,这里代码不做阐明。

1.3.4 getId() 办法

getId() 办法的作用是获得线程的惟一的惟一标示。

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName()+ ":"+ Thread.currentThread().getId());
    }
}

运行后果:

main: 1

1.3.5 yield()办法

yield()办法的作用是放弃以后的 CPU 资源,将它让给其余的工作去占用 CPU 工夫。然而放弃的工夫是不确定的,有可能刚刚放弃,马上又取得 CPU 工夫片。

创立 MyThread.java代码如下:

public class MyThread extends Thread {

    @Override
    public void run() {long beginTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 1000000; i++) {//Thread.yield();
            count+=count;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:"+(endTime-beginTime)+ "毫秒!");
    }
}

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) throws InterruptedException {MyThread myThread=new MyThread();
        myThread.start();}
}

程序运行后后果:

用时:3 毫秒!

去掉正文代码,再次运行,后果如下:阐明将 CPU 让给其余资源导致速度变慢。

用时:323 毫秒!

1.4 进行线程

进行一个线程意味着在线程解决完工作之前停掉正在做的操作,也就是放弃以后的操作。

在 Java 中有一下三种形式能够终止正在运行的线程:

  • 应用退出标示,使线程失常退出,也就是当 run 办法实现后线程终止。
  • 应用 stop 办法强行终止线程,因为 stop 和 suspend 及 resume 一样,都是作废的办法,应用起来起来 kennel 产生不可意料的后果。
  • 应用 interrupt 办法中断线程(罕用)

大多数进行一个线程的操作应用 Thread.interrupt()办法,只管办法的名称是“进行,停止”的意思,然而这个办法不会进行一个正在运行的线程,还须要退出一个判断才能够实现线程的进行操作。

1.4.1 进行不了的线程

应用 interrupt() 办法进行线程,然而 interrupt() 放大不像 for+break 语句那样,能够马上就进行循环,调用 interrupt() 办法 仅仅是在以后线程中打了一个进行的标记,并不是真正的进行线程。

创立 MyThread.java代码如下:

public class MyThread extends Thread {
    @Override
    public void run() {for (int i = 0; i <= 10000; i++) {System.out.println("i="+i);
        }
    }
}

运行类 Test.java 代码如下:

public class Test {public static void main(String[] args) throws InterruptedException {MyThread myThread=new MyThread();
        myThread.start();
        myThread.interrupt();}
}

局部运行后果:

i=9997
i=9998
i=9999
i=10000

从运行的后果来看,调用 interrupt 办法并没有进行线程,那么如何进行线程呢?

1.4.2 判断线程是否是进行状态

判断线程的状态是不是进行的,在 Java 的 JDK 中,Thread.java类中提供了两种办法:

this.isInterrupted():测试以后线程是否曾经中断。
this.isInterrupted(); 测试线程是否曾经中断。

public static boolean interrupted() 
public boolean isInterrupted()

一个是动态的办法一个不是动态的办法。

上面咱们阐明如何是 main 线程产生中断的成果呢?创立 Test.java 类,代码如下:

public class Test {public static void main(String[] args) throws InterruptedException {Thread.currentThread().interrupt();
        System.out.println("是否进行 1:"+Thread.interrupted());
        System.out.println("是否进行 2:"+Thread.interrupted());
    }
}

程序运行后后果:

是否进行 1:true
是否进行 2:false

从运行的后果来看,interrupted办法判断以后线程是否是进行状态。然而为什么第二个布尔值为 false 呢,这是因为 interrupted 办法有革除状态的性能,所以第二次调用的返回值是 false。而 isInterrupted 判断线程是否中断不革除状态标示。

接下来咱们通过一个示例来阐明如何进行一个线程,创立 MyThread.java代码如下:

public class MyThread extends Thread {

    @Override
    public void run() {
        int count = 0;
        while (!Thread.currentThread().isInterrupted()) {count++;}
        System.out.println("循环次数:" + count + ",线程中断,中断信号:" + Thread.currentThread().isInterrupted());

    }
}

创立 Test.java 类,代码如下:

public class Test {public static void main(String[] args) throws InterruptedException {MyThread myThread=new MyThread();
        myThread.start();
        Thread.sleep(5);
        myThread.interrupt();}
}

程序运行后果:

循环次数:88528,线程中断,中断信号:true

以上后果能够看到循环 88528 次后,线程收到了中断信号(即 Thread.currentThread().isInterrupted() 返回的后果为 true),循环条件不满足条件,退出循环,执行完程序,主动进行线程,这种就属于通过 interrupt 正确进行线程的状况。

1.5 暂停线程

1.5.1 suspend 与 resume 办法的应用

暂停线程意味着此线程还能够复原运行,在 Java 多线程中,能够应用 suspend 办法暂停线程,应用 resume 办法复原线程的执行。

创立 MyThread.java代码如下:

public class MyThread 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++;}
    }
}

创立 Test.java 类,代码如下:

public class Test {public static void main(String[] args) throws InterruptedException {MyThread myThread=new MyThread();
        myThread.start();
        Thread.sleep(1000);
        // A 段
        myThread.suspend();
        System.out.println("A="+System.currentTimeMillis()+ "i="+myThread.getI());
        Thread.sleep(1000);
        System.out.println("A="+System.currentTimeMillis()+ "i="+myThread.getI());

        myThread.resume();
        Thread.sleep(1000);

        myThread.suspend();
        System.out.println("B="+System.currentTimeMillis()+ "i="+myThread.getI());
        Thread.sleep(1000);
        System.out.println("B="+System.currentTimeMillis()+ "i="+myThread.getI());
    }
}

程序运行后果:

A= 1618220660051 i=690427887
A= 1618220661056 i=690427887
B= 1618220662061 i=1410759540
B= 1618220663066 i=1410759540

从程序运行后果来看,线程确实是被暂停了,而且还能够复原成运行的状态。

1.6 线程的优先级

在操作系统中,线程能够划分优先级,优先级较高的线程失去 CPU 资源较多,也就是 CPU 优先执行优先级较高的线程对象中的工作。

设置线程优先级有助于“线程布局器”,确定下一次抉择哪个线程来优先执行。

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

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 中,线程优先级分为 1~~10 这 10 个等级,如果小于 1 或者大于 10,则 JDK 抛出异样为 throw new IllegalArgumentException()

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

1.6.1 优先级的继承个性

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

创立 MyThread1.java代码如下:

public class MyThread1 extends Thread{
    @Override
    public void run() {System.out.println("MyThread1 run Priority="+this.getPriority());
        MyThread2 myThread2=new MyThread2();
        myThread2.start();}
}

创立 MyThread2.java代码如下:

public class MyThread2 extends Thread{
    @Override
    public void run() {System.out.println("MyThread2 run Priority="+this.getPriority());
    }
}

创立 Test.java 类,代码如下:

public class Test {public static void main(String[] args) {System.out.println("main thread begin  priority="+Thread.currentThread().getPriority());
        //Thread.currentThread().setPriority(8);
        System.out.println("main thread end priority="+Thread.currentThread().getPriority());
        MyThread1 myThread1=new MyThread1();
        myThread1.start();}
}

运行后果:

main thread begin priority=5
main thread end priority=5
MyThread1 run Priority=5
MyThread2 run Priority=5

将代码 Thread.currentThread().setPriority(8) 的正文去掉,再次运行 Test.java 文件,显示如下:

main thread begin priority=5
main thread end priority=8
MyThread1 run Priority=8
MyThread2 run Priority=8

1.6.2 优先级具备规则性

尽管应用 setPriority()办法能够设置线程的优先级,但还没有看到设置优先级带来的成果。

创立 MyThread1.java代码如下:

public class MyThread1 extends Thread{
    @Override
    public void run() {long beginTimeMillis = System.currentTimeMillis();
        long addResult=0;
        for (int i = 0; i < 10; i++) {for (int j = 0; j < 50000; j++) {Random random=new Random();
                random.nextInt();
                addResult=addResult+j;
            }
        }
        long endTimeMillis = System.currentTimeMillis();
        System.out.println("@@@@@@@@@@@@@@@@@@  thread 1 use time ="+ (endTimeMillis-beginTimeMillis) );
    }
}

创立 MyThread2.java代码如下:

public class MyThread2 extends Thread{
    @Override
    public void run() {long beginTimeMillis = System.currentTimeMillis();
        long addResult=0;
        for (int i = 0; i < 10; i++) {for (int j = 0; j < 50000; j++) {Random random=new Random();
                random.nextInt();
                addResult=addResult+j;
            }
        }
        long endTimeMillis = System.currentTimeMillis();
        System.out.println("@@@@@@@@@  thread 2 use time ="+ (endTimeMillis-beginTimeMillis) );
    }
}

创立 Test.java 类,代码如下:

public class Test {public static void main(String[] args) {for (int i = 0; i <5 ; i++) {MyThread1 myThread1=new MyThread1();
            myThread1.setPriority(10);
            myThread1.start();
            MyThread2 myThread2=new MyThread2();
            myThread2.setPriority(1);
            myThread2.start();}

    }
}

程序运行的后果:

@@@@@@@@@@@@@@@@@@ thread 1 use time = 412
@@@@@@@@@ thread 2 use time = 426
@@@@@@@@@@@@@@@@@@ thread 1 use time = 438
@@@@@@@@@ thread 2 use time = 449
@@@@@@@@@@@@@@@@@@ thread 1 use time = 449
@@@@@@@@@@@@@@@@@@ thread 1 use time = 455
@@@@@@@@@ thread 2 use time = 459
@@@@@@@@@ thread 2 use time = 461
@@@@@@@@@@@@@@@@@@ thread 1 use time = 462
@@@@@@@@@ thread 2 use time = 463

从运行的后果咱们发现,高优先级的线程总是大部分先执行完,但不代表优先级高的全副执行完。另外,不要认为 MyThread1 线程先被 main 线程调用就先执行完,呈现这样的后果是因为 MyThread1 线程的优先级高。当线程优先级差距较大时,谁先执行完和代码的调用程序无关。同时阐明线程的优先级具备肯定的规则性,也就是 CPU 尽量将执行资源让给优先级比拟高的线程。

1.6.3 优先级具备随机性

因为优先级具备随机性,也就是优先级比拟高的线程不肯定每一次都先执行完。

咱们将下面 Test.java 类代码 myThread2.setPriority(5),运行代码后果如下:

@@@@@@@@@@@@@@@@@@ thread 1 use time = 440
@@@@@@@@@@@@@@@@@@ thread 1 use time = 464
@@@@@@@@@@@@@@@@@@ thread 1 use time = 465
@@@@@@@@@ thread 2 use time = 466
@@@@@@@@@ thread 2 use time = 472
@@@@@@@@@ thread 2 use time = 473
@@@@@@@@@ thread 2 use time = 475
@@@@@@@@@@@@@@@@@@ thread 1 use time = 476
@@@@@@@@@ thread 2 use time = 476
@@@@@@@@@@@@@@@@@@ thread 1 use time = 476

那么,咱们得出一个论断,不要把线程的优先级与运行后果的程序作为掂量的规范,优先级较高的不肯定每一次都先执行完。也就是说优先级与打印的程序无关,因为它们的关系具备不确定性和随机性。

1.7 守护线程

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

守护线程是一种非凡的线程,它的个性有“陪伴”的含意,当过程中不存在非守护线程了。则守护线程主动销毁。典型的守护线程就是 垃圾回收线程

艰深的说:“守护线程”:任何守护线程都是整个 JVM 中非守护线程的“保姆”,只有以后 JVM 实例中存在任何一个非守护线程没有完结,守护线程就在工作,只有当最初一个非守护线程完结时,守护线程才随 JVM 一起完结工作。Daemon 的作用是为其余线程的运行提供便当服务,守护线程最典型的利用就是 GC(垃圾回收器)。

创立 MyThread.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();
        }

    }
}

创立 Test.java 类,代码如下:

public class Test {public static void main(String[] args) throws  InterruptedException{MyThread myThread=new MyThread();
       myThread.setDaemon(true);
       myThread.start();
       Thread.sleep(5000);
        System.out.println("我来到 myThread 对象也不打印了,也进行了");
    }
}

程序运行的后果:

i= 1
i= 2
i= 3
i= 4
i= 5
我来到 myThread 对象也不打印了,也进行了

看到这里明天的分享就完结了,如果感觉这篇文章还不错,来个 分享、点赞、在看 三连吧,让更多的人也看到~

欢送关注集体公众号 「JavaClub」,定期为你分享一些技术干货。

正文完
 0