第一章 Java多线程技能
1)线程的启动;
2)如何使线程暂停;
3)线程的优先级;
4)线程安全相关的问题
1.1 进程和多线程的概念及多线程的优点
进程:受操作系统管理的基本运行单元。
线程:进程中独立运行的子任务。
image多线程的优点:
在图1-1-1中,任务1与任务2是两个完全独立,互不相关的任务,任务1在等待远程服务器返回数据,这时CPU一直“空运行”,必须要等到任务1执行完毕后才执行任务2。以上概括了单任务的特点:排队执行
,也就是同步。这就是单任务环境的缺点,CPU利用率大幅降低。
在图1-1-2可以发现,CPU完全可以再任务1与任务2之间来回切换,使任务2不必等待任务1执行完毕再进行,系统的运行效率大大提升。
1.2 使用多线程
实现多线程的的方式有两种,一种是继承
Thread
类,另一种是实现Runnable
接口。
1.2.1 继承Thread类
public class Thread implements Runnable{}
使用继承Thread类的方式来创建多线程时,最大的局限性就是不支持多继承(Java语言的特点就是单根继承)。下面使用第一种方式,继承Thread类。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
public class Run{
public static void main(String[] args) {
MyThread myThread =new MyThread();
myThread.start();
System.out.println("运行结束");
}
}
运行结束
MyThread
从执行结果来看,MyThread类中run方法执行的时间较晚,这也说明在使用多线程技术时,代码的运行结果与代码的执行顺序或调用顺序是无关的。线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法,所以就会出现如上结果。 如果多次调用start(),则会出现
Exception in thread "main" java.lang.IllegalThreadStateException
。
上面介绍了线程调用的随机性,下面我们再演示线程的随机性,如下。
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();
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread myThread =new MyThread();
myThread.setName("MyThread");
myThread.start();
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.fillInStackTrace();
}
}
}
run=MyThread
main=main
main=main
run=MyThread
run=MyThread
main=main
run=MyThread
run=MyThread
main=main
main=main
继承Thread,重写run方法,调用start方法执行线程的流程:调用Thread类中的start方法通知
线程规划器
此线程已经准备就绪,等待调用线程对象的run方法。这个过程其实就是让系统安排一个时间来调用Thread中的run方法,使线程得到运行,线程为异步执行;如果直接调用Thread.run(),那么此线程对象并不交给“线程规划器”来处理,而是由main主线程来调用run(),也就是同步执行。
1.2.2 实现Runnable接口
如果预创建的线程类已经有一个父类了,这时候就不能再继承Thread了,因为Java不支持多继承,所以就需要实现Runnbale接口来应对这种情况。 如何使用Runnable接口呢,那么就要看一下Thread的构造函数了。
public Thread(){}
public Thread(Runnable target){}
public Thread(ThreadGroup group, Runnable target){}
public Thread(String name){}
public Thread(ThreadGroup group, String name){}
public Thread(Runnable target, String name){}
public Thread(ThreadGroup group, Runnable target, String name){}
public Thread(ThreadGroup group, Runnable target, String name, long stackSize){}
在Thread类的8个构造函数中,有两个个构造函数
Thread(Runnable target)
Thread(Runnable target, String name)
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("running");
}
}
public class RunMyRunnable{
public static void main(String[] args) {
Thread thread =new Thread(new MyRunnable());
System.out.println(thread.getName());
thread.start();
Thread thread2 =new Thread(new MyRunnable(),"customize name");
System.out.println(thread2.getName());
thread2.start();
System.out.println("运行结束");
}
}
Thread-0
customize name
运行结束
running
running
1.2.3 实例变量与线程安全
自定义线程类中的实例变量针对其他线程有
共享
和不共享
之分,这在多线程之间进行交互时是很重要的一个技术点。
1.2.3.1 不共享数据的情况
public class MyThread extends Thread{
private int count =5;
public MyThread(String name) {
this.setName(name);
}
@Override
public void run() {
super.run();
while (count >0) {
count--;
System.out.println("由" +this.currentThread().getName() +"计算,count=" +count);
}
}
}
public class Run{
public static void main(String[] args) {
MyThread myThreadA =new MyThread("myThreadA");
MyThread myThreadB =new MyThread("myThreadB");
MyThread myThreadC =new MyThread("myThreadC");
myThreadA.start();
myThreadB.start();
myThreadC.start();
}
}
由myThreadA计算,count=4
由myThreadC计算,count=4
由myThreadC计算,count=3
由myThreadC计算,count=2
由myThreadC计算,count=1
由myThreadC计算,count=0
由myThreadA计算,count=3
由myThreadA计算,count=2
由myThreadA计算,count=1
由myThreadA计算,count=0
由myThreadB计算,count=4
由myThreadB计算,count=3
由myThreadB计算,count=2
由myThreadB计算,count=1
由myThreadB计算,count=0
1.2.3.2 共享数据的情况
共享数据是指多个线程同时访问同一个变量。
public class MyThread extends Thread{
private int count =5;
@Override
public void run() {
super.run();
count--;
System.out.println("由" +this.currentThread().getName() +"计算,count=" +count);
}
}
public class Run{
public static void main(String[] args) {
MyThread myThreadA =new MyThread();
Thread a =new Thread(myThreadA,"A");
Thread b =new Thread(myThreadA,"B");
Thread c =new Thread(myThreadA,"C");
Thread d =new Thread(myThreadA,"D");
Thread e =new Thread(myThreadA,"E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
由C计算,count=3
由B计算,count=3
由A计算,count=2
由E计算,count=1
由D计算,count=0
线程C和B打印出的count值都是3,说明A和B同时获取到count并进行--的操作,产生了
非线程安全
问题。count--的操作分成三步:
1)取得原有count值;
2)计算count-1;
3)对count赋值。
如果多个线程同时访问,一定会出现非线程安全
问题。
那么如何解决上面的线程安全问题呢?这时就需要使多个线程之间进行同步。更改MyThread类代码如下:
public class MyThread extends Thread{
private int count =5;
@Override
synchronized public void run() {
super.run();
count--;
System.out.println("由" +this.currentThread().getName() +"计算,count=" +count);
}
}
由A计算,count=4
由E计算,count=3
由B计算,count=2
由D计算,count=1
由C计算,count=0
运行之后,执行结果与预期一致。通过在run方法前加入
synchronized
,使多个线程在执行run方法时,以排队的方式进行处理。当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他线程正在调用run方法,必须等其他线程对run方法调用结束之后才可以执行。这样也就实现了排队调用run方法的目的。synchronized
可以在任意对象及方法上加锁,而加锁的这段代码成为互斥区
或临界区
。
当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能拿到这把锁,那么这个线程就可以执行
synchronized
里面的代码。如果不能获取这把锁,那么这个线程就会不断地尝试拿这把锁,直到拿到为止。
非线程安全
主要是指多个线程对同一对象中的同一实例变量进行操作时,会出现值被更改,值不同步的情况,进而影响程序的执行流程。
下面来实现一个非线程安全导致的后果。
public class LoginServlet{
private static StringuserNameRef;
private static StringpasswordRef;
public static void login(String userName, String password) {
userNameRef = userName;
try {
if (userName.equals("a")) {
Thread.sleep(5000);
}
passwordRef = password;
System.out.println("当前的用户名:" +userNameRef +" 密码:" +passwordRef);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ALogin extends Thread{
@Override
public void run() {
LoginServlet.login("a", "aa");
}
}
public class BLogin extends Thread{
@Override
public void run() {
LoginServlet.login("b", "bb");
}
}
public class DoLogin{
public static void main(String[] args) {
ALogin a =new ALogin();
a.start();
BLogin b =new BLogin();
b.start();
}
}
当前的用户名:b 密码:bb
当前的用户名:b 密码:aa
解决这个
非线程安全
的方法也是使用synchronized
,更改代码如下:
public class LoginServlet{
private static StringuserNameRef;
private static StringpasswordRef;
synchronized public static void login(String userName, String password) {
userNameRef = userName;
try {
if (userName.equals("a")) {
Thread.sleep(5000);
}
passwordRef = password;
System.out.println("当前的用户名:" +userNameRef +" 密码:" +passwordRef);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
当前的用户名:a 密码:aa
当前的用户名:b 密码:bb
1.3 currentThread()
currentThread方法可返回代码段正在被哪个线程调用的信息。
1.4 isAlive()
判断当前线程是否处于活动状态。活动状态就是线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就认为线程是“存活的”。
1.5 sleep()
在指定得毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。
1.6 getId()
getId()方法的作用是取得线程的唯一标识。
1.7 停止线程
停止一个线程意味着在线程处理完任务之前停掉正在做的操作。虽然看起来很简单,但是必须做好防范措施,以便达到预期效果。
在Java中有三种方法可以终止正在运行的线程:
1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2)使用stop
方法(已经被作废)强行终止线程。但是不推荐使用此方法,因为stop
和susped
、resume
一样都是作废过期的方法。
3)使用interrupt
方法中断线程。
1.7.1 停止不了的线程
interrupt
方法仅是在当前线程中打了一个停止的标记,并不是真的停止线程。
public class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i =0; i <500000; i++) {
System.out.println("i=" + i);
}
}
}
public class Run{
public static void main(String[] args) {
MyThread thread =new MyThread();
thread.start();
thread.interrupt();
}
}
从运行结果来看,调用interrupt方法并没有停止线程。
1.7.2 判断线程是否是停止状态
1)
this.interrupted()
:测试当前线程是否已经中断,执行后具有将状态标志清除为false的功能。
2)this.isInterrupted()
:测试当前线程是否已经中断,不清除状态标志。
1.7.3 能停止的线程-异常法
public class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i =0; i <500000; i++) {
if (this.isInterrupted()) {
System.out.println("停止状态 退出");
break;
}
System.out.println("i=" + i);
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (Exception e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
i=408980
i=408981
i=408982
i=408983
i=408984
end
停止状态 退出
上面示例代码虽然停止了线程但如果for下面还有语句,还是会运行的,该如何解决语句继续运行的问题呢 ?
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
for (int i =0; i <500000; i++) {
if (this.isInterrupted()) {
System.out.println("停止状态 退出");
throw new InterruptedException();
}
System.out.println("i=" + i);
}
System.out.println("for之后运行,线程并未停止");
} catch (Exception e) {
System.out.println("MyThread catch");
e.printStackTrace();
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (Exception e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
i=410919
i=410920
i=410921
end
停止状态 退出
MyThread catch
java.lang.InterruptedException
1.7.4 在沉睡中停止
如果线程在
sleep
状态下停止线程,会是什么效果呢?
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(20000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!进入catch!" +this.isInterrupted());
e.printStackTrace();
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (Exception e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
run begin
end
在沉睡中被停止!进入catch!false
java.lang.InterruptedException: sleep interrupted
在
sleep
状态下,停止某一线程,会进入catch语句,并清除停止状态值,使之变成false
。
1.7.5 能停止的线程-暴力停止
使用
stop
方法停止线程则是非常暴力的。
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 (Exception e) {
e.printStackTrace();
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(8000);
thread.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
1.7.6 stop()与java.lang.ThreadDeath异常
调用
stop()
时会抛出java.lang.ThreadDeath
异常。该方法已经被作废,因为如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了解锁
,导致数据得不到同步的处理,出现数据不一致的问题。
1.7.7 释放锁的不良后果
使用
stop()释放锁
将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行流程错误,一定要特别注意。
public class SynchronizedObject{
private String userName ="a";
private String password ="a";
public String get UserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void printString(String userName, String password) {
try {
this.userName = userName;
Thread.sleep(100000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyThread extends Thread{
private SynchronizedObject syncObject;
public MyThread(SynchronizedObject syncObject) {
this.syncObject = syncObject;
}
@Override
public void run() {
syncObject.printString("b", "bbb");
}
}
public class Run{
public static void main(String[] args) {
try {
SynchronizedObject syncObject =new SynchronizedObject();
MyThread thread =new MyThread(syncObject);
thread.start();
Thread.sleep(8000);
thread.stop();
System.out.println(syncObject.getUserName() +" " +syncObject.getPassword());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
b a
1.7.8 使用return停止线程
interrupt
与return
结合使用也能实现停止线程的效果。
public class MyThread extends Thread{
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("stop");
return;
}
System.out.println("current time=" + System.currentTimeMillis());
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(8000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
current time=1587436071260
current time=1587436071260
stop
不过还是建议使用抛异常的方法来实现线程的停止,因为在catch块中还可以讲一场向上抛,是线程停止的时间得以传播。
1.8 暂停线程
暂停线程意味着此线程还可以恢复运行,在Java多线程中,可以使用
suspend()
暂停线程,使用resume()
恢复线程的执行。
1.8.1 suispend与resume方法的使用
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++;
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(5000);
// 暂停线程
thread.suspend();
System.out.println("A=" + System.currentTimeMillis() +" i=" + thread.getI());
Thread.sleep(5000);
System.out.println("A=" + System.currentTimeMillis() +" i=" + thread.getI());
// 恢复
thread.resume();
Thread.sleep(5000);
// 继续暂停
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();
}
}
}
A=1587436765560 i=2885201074
A=1587436770560 i=2885201074
B=1587436775560 i=5820785533
B=1587436780560 i=5820785533
1.8.2 suspend与resume方法的缺点-独占
在使用
suspend
与resume
方法时,如果使用不当,极易造成公共的同步对象的独占
,是其他线程无法访问公共同步对象。
public class SynchronizedObject{
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");
}
}
public class Run{
public static void main(String[] args) {
try {
final SynchronizedObject syncObject =new SynchronizedObject();
Thread thread =new Thread(new Runnable(){
@Override
public void run() {
syncObject.printString();
}
});
thread.setName("a");
thread.start();
Thread.sleep(1000);
Thread thread2 =new Thread(new Runnable(){
@Override
public void run() {
System.out.println("thread2 begin");
syncObject.printString();
}
});
thread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
begin
a线程一直suspend···
thread2 begin
还有另外一种独占锁的情况也要格外注意,稍有不慎就会进坑。
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++;
System.out.println(i);
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread myThread =new MyThread();
myThread.start();
Thread.sleep(1000);
myThread.suspend();
System.out.println("main end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
程序不会再打印“main end”,因为当程序运行到println()方法停止时,同步锁未释放。println()源码如下:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
1.8.3 suspend与resume方法的缺点-不同步
在使用
suspend
与resume
方法是容易出现因线程暂停而导致数据不同步的情况。
public class MyObject{
private String userName ="1";
private String password ="11";
public void setValue(String userName, String password) {
this.userName = userName;
if ("a".equals(Thread.currentThread().getName())) {
System.out.println("线程a暂停");
Thread.currentThread().suspend();
}
this.password = password;
}
public void print() {
System.out.println(userName +" " +password);
}
}
public class Run{
public static void main(String[] args) throws InterruptedException{
final MyObject myObject =new MyObject();
Thread thread =new Thread(new Runnable(){
@Override
public void run() {
myObject.setValue("lala", "lalala");
}
});
thread.setName("a");
thread.start();
Thread.sleep(1000);
new Thread(new Runnable(){
@Override
public void run() {
myObject.print();
}
}).start();
}
}
线程a暂停
lala 11
1.9 yield方法
yield
方法的作用是放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。单放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
public class MyThread extends Thread{
@Override
public void run() {
long beginTime = System.currentTimeMillis();
int count =0;
for (int i =0; i <50000000; i++) {
// Thread.yield();
count = count + i +1;
}
long endTime = System.currentTimeMillis();
System.out.println("用时:" +(endTime - beginTime) +"毫秒!");
}
}
public class Run{
public static void main(String[] args) {
MyThread myThread =new MyThread();
myThread.start();
}
}
用时:19毫秒!
取消
Thread.yield();
注释,再次运行结果如下所示:
用时:11723毫秒!
1.10 线程的优先级
操作系统中,线程和一划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU有限制性优先级较高的线程对象中的任务。设置线程优先级有助于帮
线程规划器
确定下一次选择哪一个线程来优先执行。设置线程优先级使用setPriority()
,源码如下:
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority >MAX_PRIORITY || newPriority
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) !=null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
在Java中,线程的优先级分为1至10,这十个等级,如果小于或大于10,则抛出异常
throw new IllegalArgumentException()
。
JDK中使用三个常量来预定义优先级的值,代码如下:
/**
* 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;
1.10.1 线程优先级的继承特性
在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。
public class MyThread2 extends Thread{
@Override
public void run() {
System.out.println("myThread2 run priority="+this.getPriority());
}
}
public class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("myThread1 run priority="+this.getPriority());
MyThread2 thread2 =new MyThread2();
thread2.start();
}
}
public class Run{
public static void main(String[] args) {
System.out.println("main thread begin priority="+Thread.currentThread().getPriority());
Thread.currentThread().setPriority(6);
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=6
myThread1 run priority=6
myThread2 run priority=6
1.10.2 优先级具有规则性
public class MyThread1 extends Thread{
@Override
public void run() {
long beginTime = System.currentTimeMillis();
for (int i =0; i <100; i++) {
Random random =new Random();
random.nextInt();
}
long endTime = System.currentTimeMillis();
System.out.println("★★★★★ thread1 use time=" +(endTime - beginTime));
}
}
public class MyThread2 extends Thread{
@Override
public void run() {
long beginTime = System.currentTimeMillis();
for (int i =0; i <100; i++) {
Random random =new Random();
random.nextInt();
}
long endTime = System.currentTimeMillis();
System.out.println("☆☆☆☆☆ thread2 use time=" +(endTime - beginTime));
}
}
public class Run{
public static void main(String[] args) {
for (int i =0; i <5; i++) {
MyThread1 thread1 =new MyThread1();
thread1.start();
MyThread2 thread2 =new MyThread2();
thread2.setPriority(6);
thread2.start();
}
}
}
第一次执行结果:
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=0
★★★★★ thread1 use time=0
☆☆☆☆☆ thread2 use time=0
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=0
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=4
★★★★★ thread1 use time=3
☆☆☆☆☆ thread2 use time=4
第二次执行结果:
★★★★★ thread1 use time=0
★★★★★ thread1 use time=0
★★★★★ thread1 use time=1
★★★★★ thread1 use time=0
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=2
☆☆☆☆☆ thread2 use time=1
☆☆☆☆☆ thread2 use time=0
☆☆☆☆☆ thread2 use time=0
☆☆☆☆☆ thread2 use time=0
根据以上时长可以得出,不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级较高的线程不一定每次都先执行完run()方法中的任务。他们的关系具有
不确定性
和随机性
。
1.11 守护线程
Java中有两种线程,一种是
用户线程
,一种是守护线程
。
当进程中不存在费守护线程了,则守护线程自动销毁。典型的守护线程就是GC
(垃圾回收器)。任何一个守护线程都是整个JVM中所有费守护线程的“保姆”,只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。
public class MyThread extends Thread{
private long i =0;
@Override
public void run() {
try {
while (true) {
i++;
System.out.println("i=" +i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run{
public static void main(String[] args) {
try {
MyThread myThread =new MyThread();
myThread.setDaemon(true);
myThread.start();
Thread.sleep(5000);
System.out.println("main thread end, myThread end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
网友评论