我是阿福,公众号「<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」,定期为你分享一些技术干货。