Thread图解
Thread类包含方法过多,目前只截取部分
Thread中的实例方法
Thread类中的方法调用方式:
1、this.XXX()
这种调用方式表示的线程是线程实例本身
2、Thread.currentThread.XXX()或Thread.XXX()
这种调用方式表示的线程是正在执行Thread.currentThread.XXX()所在代码块的线程
以下主要从Thread类中的实例方法和类方法的角度讲解Thread中的方法。
Thread类中的实例方法
实例方法,只和实例线程(也就是new出来的线程)本身挂钩,和当前运行的是哪个线程无关。
1、start()
start()方法的作用讲得直白点就是通知“线程规划期”,此线程可以运行了,正在等待CPU调用线程对象得run()方法,产生一个异步执行的效果。通过start()方法产生得到结论。
示例1:
package main.thread; public class ThreadMethodTest { public static void main(String[] args) { MyThread02 myThread02 = new MyThread02(); myThread02.start(); try{ for (int i = 0; i < 3;i++){ Thread.sleep((int)(Math.random() * 1000)); System.out.println("main method run = " + Thread.currentThread().getName()); } }catch (Exception e){ e.printStackTrace(); } } } class MyThread02 extends Thread { public void run(){ try{ for(int i = 0; i < 3; i++){ Thread.sleep((int)(Math.random() * 1000)); System.out.println("MyThread02 run method run = " + Thread.currentThread().getName()); } }catch (Exception e){ e.printStackTrace(); } } }
执行结果:
MyThread02 run method run = Thread-0 main method run = main MyThread02 run method run = Thread-0 MyThread02 run method run = Thread-0 main method run = main main method run = main
结果表明:CPU执行哪个线程的代码具有不确定性。
示例2:
package main.thread; public class ThreadMethodTest2 { public static void main(String[] args) { MyThread03 myThread03_0 = new MyThread03(); MyThread03 myThread03_1 = new MyThread03(); MyThread03 myThread03_2 = new MyThread03(); myThread03_0.start(); myThread03_1.start(); myThread03_2.start(); } } class MyThread03 extends Thread{ public void run(){ System.out.println("MyThread03 run method, threadName = " + Thread.currentThread().getName()); } }
执行结果:
MyThread03 run method, threadName = Thread-0 MyThread03 run method, threadName = Thread-2 MyThread03 run method, threadName = Thread-1
尽管线程是按照myThread03_0,myThread03_1,myThread03_2 ,但实际上的启动顺序是myThread03_0,myThread03_2,myThread03_1,该例子表明:调用start()方法的顺序不代表线程启动的顺序,线程启动顺序具有不确定性。
2、run()
线程开始执行,虚拟机调用的是线程run()方法中的内容。
示例:
package main.thread; public class ThreadMethodTest { public static void main(String[] args) { MyThread02 myThread02 = new MyThread02(); //非异步执行, 方法调用 myThread02.run(); try{ for (int i = 0; i < 3;i++){ Thread.sleep((int)(Math.random() * 1000)); System.out.println("main method run = " + Thread.currentThread().getName()); } }catch (Exception e){ e.printStackTrace(); } } } class MyThread02 extends Thread { public void run(){ try{ for(int i = 0; i < 3; i++){ Thread.sleep((int)(Math.random() * 1000)); System.out.println("MyThread02 run method run = " + Thread.currentThread().getName()); } }catch (Exception e){ e.printStackTrace(); } } }
执行难结果:
MyThread02 run method run = main MyThread02 run method run = main MyThread02 run method run = main main method run = main main method run = main main method run = main
看到打印了6次的"run = main",说明如果只有run()没有start(),Thread实例run()方法里面的内容是没有任何异步效果的,全部被main函数执行。换句话说,只有run()而不调用start()启动线程是没有任何意义的。
3、isAlive()
测试线程是否属于活动状态,只要线程启动且没有终止,方法返回的就是true。
示例:
package main.thread; public class ThreadMethodIsAliveTest { public static void main(String[] args) { MyThread04 myThread04 = new MyThread04(); System.out.println("before myThread04 start, isAlive="+myThread04.isAlive()); myThread04.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("after myThread04 start, isAlive="+myThread04.isAlive()); } } class MyThread04 extends Thread{ public void run(){ System.out.println("MyThread04 run, isAlive="+this.isAlive()); } }
执行结果:
before myThread04 start, isAlive=false MyThread04 run, isAlive=true after myThread04 start, isAlive=false
在start()之前,线程的isAlive是false,start()之后就是true了。main函数中加上Thread.sleep(100)的原因是为了确保Thread04的run()方法中的代码执行完,否则可能end这里打印出来的是true,可以尝试验证下哈。
4、getId()
在一个java应用中,有一个long型的全局唯一的线程ID生成器threadSeqNumber,每new出来一个线程都会把这个自增一次,并赋予线程的tid属性,这个是Thread自己做的,用户无法执行一个线程的Id。
5、getName()
我们new一个线程的时候,可以指定该线程的名字,也可以不指定。若指定,那么线程的名字就是我们自己指定的,getName()返回的也是开发者指定的线程的名字;若不指定,那么Thread中有一个int型全局唯一的线程初始号生成器threadInitNum,Java先把threadInitNum自增,然后以“”hread-threadInitNum"的方式来命名新生成的线程。
示例:
package main.thread; public class ThreadMethodTest2 { public static void main(String[] args) { MyThread03 myThread03_0 = new MyThread03(); MyThread03 myThread03_1 = new MyThread03(); MyThread03 myThread03_2 = new MyThread03(); myThread03_0.setName("name0"); myThread03_1.setName("name1"); myThread03_2.setName("name2"); myThread03_0.start(); myThread03_1.start(); myThread03_2.start(); } } class MyThread03 extends Thread{ public void run(){ System.out.println("MyThread03 run method, threadName = " + Thread.currentThread().getName() + " threadTid="+ Thread.currentThread().getId()); } }
执行结果:
MyThread03 run method, threadName = name0 threadTid=11 MyThread03 run method, threadName = name2 threadTid=13 MyThread03 run method, threadName = name1 threadTid=12
6、getPriority()和setPriority(int newPriority)
/**
* 线程的优先级属性
*/
private int priority;
/**
* 线程所能拥有的最大优先级.
*/
public final static int MIN_PRIORITY = 1;
/**
* 线程默认的优先级.
*/
public final static int NORM_PRIORITY = 5;
/**
* 线程所能拥有的最大优先级.
*/
public final static int MAX_PRIORITY = 10;
这两个方法用于获取和设置线程的优先级,优先级高的CPU得到的CPU资源比较多,设置优先级有助于帮"线程规划器"确定下一次选择哪一个线程优先执行。换句话说,两个在等待CPU的线程,优先级高的线程越容易被CU选择执行。
示例:
package main.thread; public class ThreadMethodPriorityTest { public static void main(String[] args) { System.out.println("main thread begin priority=" + Thread.currentThread().getPriority()); System.out.println("main thread end priority=" + Thread.currentThread().getPriority()); PriorityThread_1 priorityThread_1 = new PriorityThread_1(); priorityThread_1.start(); } } class PriorityThread_0 extends Thread{ public void run(){ System.out.println("PriorityThread_0 run priority=" + this.getPriority()); } } class PriorityThread_1 extends Thread{ public void run(){ System.out.println("PriorityThread_1 run priority=" + this.getPriority()); PriorityThread_0 priorityThread = new PriorityThread_0(); priorityThread.start(); } }
执行结果:
main thread begin priority=5 main thread end priority=5 PriorityThread_1 run priority=5 PriorityThread_0 run priority=5
从上述示例看出:线程默认优先级为5,如果不手动指定,那么线程优先级具有继承性,比如线程A启动线程B,那么线程B的优先级和线程A的优先级相同。
再看一个手动设置priority的示例:
package main.thread; public class ThreadMethodPriorityTest { public static void main(String[] args) { System.out.println("main thread priority=" + Thread.currentThread().getPriority()); PriorityThread_0 priorityThread_0 = new PriorityThread_0(); priorityThread_0.setPriority(1); priorityThread_0.start(); PriorityThread_1 priorityThread_1 = new PriorityThread_1(); priorityThread_1.setPriority(9); priorityThread_1.start(); } } class PriorityThread_0 extends Thread{ public void run(){ System.out.println("PriorityThread_0 thread priority=" + Thread.currentThread().getPriority()); long beginTime = System.currentTimeMillis(); for (int j = 0; j < 1000000; j++){} long endTime = System.currentTimeMillis(); System.out.println("PriorityThread_0 use time = " + (endTime - beginTime)); } } class PriorityThread_1 extends Thread{ public void run(){ System.out.println("PriorityThread_1 thread priority=" + Thread.currentThread().getPriority()); long beginTime = System.currentTimeMillis(); for (int j = 0; j < 1000000; j++){} long endTime = System.currentTimeMillis(); System.out.println("PriorityThread_1 use time = " + (endTime - beginTime)); } }
执行结果:
main thread priority=5 PriorityThread_0 thread priority=1 PriorityThread_1 thread priority=9 PriorityThread_0 use time = 3 PriorityThread_1 use time = 3
上述执行的结果并不是优先级高的先执行完,只能说CPU会尽量将执行资源让给优先级比较高的线程。不要误导自己,原因如下:
因此, 在实际的编码时, 认为高优先级一定先于低优先级的线程执行, 最后会出问题的。
注意:
优先级和操作系统及虚拟机版本相关。
++优先级只是代表告知了 「线程调度器」该线程的重要度有多大。如果有大量线程都被堵塞,都在等候运
行,调试程序会首先运行具有最高优先级的那个线程。然而,这并不表示优先级较低的线程不会运行(换言之,不会因为存在优先级而导致死锁)。若线程的优先级较低,只不过表示它被准许运行的机会小一些而已。++
7、isDaemon、setDaemon(boolean on)
讲解两个方法前,首先要知道理解一个概念。Java中有两种线程,一种是用户线程,一种是守护线程。守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的应用便是GC线程。如果进程中不存在非守护线程了,那么守护线程自动销毁,因为没有存在的必要,为别人服务,结果服务的对象都没了,当然就销毁了。理解了这个概念后,看一下例子:
package main.thread; public class DaemonThreadTest { public static void main(String[] args) { DaemonThread daemonThread = new DaemonThread(); //设置为守护线程 daemonThread.setDaemon(true); daemonThread.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("非守护线程要结束了"); } } class DaemonThread extends Thread{ private int i = 0; public void run(){ try{ System.out.println("DaemonThread start"); while(true){ i ++; System.out.println("i = " + i); Thread.sleep(1000); } }catch (Exception e){ e.printStackTrace(); } } }
执行结果:
DaemonThread start i = 1 i = 2 i = 3 i = 4 i = 5 非守护线程要结束了
要解释一下。我们将DaemonThread 线程设置为守护线程,看到第7行的那句话,而i停在7不会再运行了。这说明,main线程运行了5秒结束,而i每隔1秒累加一次,5秒后main线程执行完结束了,MyThread11作为守护线程,main函数都运行完了,自然也没有存在的必要了,就自动销毁了,因此也就没有再往下打印数字。
关于守护线程,有一个细节注意下,setDaemon(true)必须在线程start()之前。
注意:守护线程在退出的时候并不会执行finnaly块中的代码,所以将释放资源等操作不要放在finnaly块中执行,这种操作是不安全的
8、interrupt()
这是一个有点误导性的名字,实际上Thread类的interrupt()方法无法中断线程。
示例:
package main.thread; public class ThreadInterruptTest { public static void main(String[] args) { InterruptThread interruptThread = new InterruptThread(); interruptThread.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("interrupt method end"); interruptThread.interrupt(); System.out.println("interrupt method end"); } } class InterruptThread extends Thread{ public void run(){ System.out.println("InterruptThread start run"); for (int i = 0; i < 1000; i++) { System.out.println("i = " + (i + 1)); } System.out.println("InterruptThread end run"); } }
执行结果:
InterruptThread start run i = 1 i = 2 ...... i = 189 interrupt method end i = 190 interrupt method end i = 191 ...... i = 1000 InterruptThread end run
看结果还是打印到了50000。也就是说,尽管调用了interrupt()方法,但是线程并没有停止。interrupt()方法的作用实际上是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞状态。换句话说,没有被阻塞的线程,调用interrupt()方法是不起作用的。
9、isInterrupted()
测试线程是否已经中断,但不清除状态标识。
10、join()
讲解join()方法之前请确保对于http://www.cnblogs.com/xrq730/p/4853932.html一文,即wait()/notify()/notifyAll()机制已熟练掌握。
join()方法的作用是等待线程销毁。如main线程的执行时间为1s,子线程的执行时间为10s,但是主线程依赖子线程执行的结果,怎么办?可以向生产者/消费者模型一样,搞一个缓冲区,子线程执行完把书放在缓冲区,通知main线程,main线程去拿,这样就不会浪费main线程的事件了。另一种方式就是join()了。
package main.thread; public class ThreadJoinTest { public static void main(String[] args) { JoinThread joinThread = new JoinThread(); joinThread.start(); try { joinThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("joinThread 执行完毕在执行该语句"); } } class JoinThread extends Thread{ public void run(){ try{ int value = (int)(Math.random() * 1000); System.out.println("JoinThread run, value = " + value); Thread.sleep(value); }catch (Exception e){ e.printStackTrace(); } } }
执行结果:
JoinThread run, value = 405
joinThread 执行完毕在执行该语句
无论执行几次 都是这样的顺序结果, 只是value = 405值不一样而已。
join()方法会使调用join()方法的线程(也就是joinThread线程)所在的线程(也就是main线程)无限阻塞,直到调用join()方法的线程销毁为止,此例中main线程就会无限期阻塞直到mt的run()方法执行完毕。
join()方法的一个重点是要区分出和sleep()方法的区别。join(2000)也是可以的,表示调用join()方法所在的线程最多等待2000ms,两者的区别在于:
sleep(2000)不释放锁,join(2000)释放锁,因为join()方法内部使用的是wait(),因此会释放锁。看一下join(2000)的源码就知道了,join()其实和join(2000)一样,无非是join(0)而已:
1 public final synchronized void join(long millis) 2 throws InterruptedException { 3 long base = System.currentTimeMillis(); 4 long now = 0; 5 6 if (millis < 0) { 7 throw new IllegalArgumentException("timeout value is negative"); 8 } 9 10 if (millis == 0) { 11 while (isAlive()) { 12 wait(0); 13 } 14 } else { 15 while (isAlive()) { 16 long delay = millis - now; 17 if (delay <= 0) { 18 break; 19 } 20 wait(delay); 21 now = System.currentTimeMillis() - base; 22 } 23 } 24 }
Thread类中的静态方法
Thread类中的静态方法表示操作的线程是“正在执行静态方法所在的代码块的线程”,为什么Thread类中要有静态方法,这样就能对CPU当前正在运行的线程进行操作。
1、currentThread
currentThread()方法返回的是对当前正在执行线程对象的引用。
示例1:
package main.thread; public class CurrentThreadMethodTest { public static void main(String[] args) { CurrentThreadMethod currentThreadMethod = new CurrentThreadMethod(); currentThreadMethod.start(); } } class CurrentThreadMethod extends Thread{ static { System.out.println("CurrentThreadMethod static模块的打印, threadName="+Thread.currentThread().getName()); } public CurrentThreadMethod(){ System.out.println("CurrentThreadMethod 构造方法的打印, threadName="+Thread.currentThread().getName()); } public void run(){ System.out.println("CurrentThreadMethod run()方法的打印, threadName="+Thread.currentThread().getName()); } }
执行结果:
CurrentThreadMethod static模块的打印, threadName=main CurrentThreadMethod 构造方法的打印, threadName=main CurrentThreadMethod run()方法的打印, threadName=Thread-0
从执行结果看出:线程类的构造方法、静态块是被main线程调用的,而线程类的run()方法才是应用线程自己调用的。
示例2:
package main.thread; public class CurrentThreadMethodTest { public static void main(String[] args) { CurrentThreadMethod currentThreadMethod = new CurrentThreadMethod(); currentThreadMethod.start(); } } class CurrentThreadMethod extends Thread{ static { System.out.println("CurrentThreadMethod static模块的打印, threadName="+Thread.currentThread().getName()); } public CurrentThreadMethod(){ System.out.println("CurrentThreadMethod 构造方法, Thread.currentThread().getName()="+Thread.currentThread().getName()); System.out.println("CurrentThreadMethod 构造方法, this.getName()="+this.getName()); } public void run(){ System.out.println("CurrentThreadMethod run()方法, Thread.currentThread().getName()="+Thread.currentThread().getName()); System.out.println("CurrentThreadMethod run()方法, this.getName()="+this.getName()); } }
执行结果:
CurrentThreadMethod static模块的打印, threadName=main CurrentThreadMethod 构造方法, Thread.currentThread().getName()=main CurrentThreadMethod 构造方法, this.getName()=Thread-0 CurrentThreadMethod run()方法, Thread.currentThread().getName()=Thread-0 CurrentThreadMethod run()方法, this.getName()=Thread-0
上篇文章的开头就说过,要理解一个重要的概念,就是"this.XXX()"和"Thread.currentThread().XXX()"的区别,这个就是最好的例子。必须要清楚的一点就是:当前执行的Thread未必就是Thread本身。从这个例子就能看出来:
(1)执行CurrentThreadMethod 构造方法是main,当前线程却是Thread-0
(2)执行run()方法的Thread-0,当前线程也是Thread-0,说明run()方法就是被线程实例去执行的
所以,再强调一下,未必在CurrentThreadMethod 里调用Thread.currentThread()返回回来的线程对象的引用就是CurrentThreadMethod
2、sleep(long millis)
sleep(long millis)方法的作用是在指定的毫秒内让当前"正在执行的线程"休眠(暂停执行)。这个"正在执行的线程"是关键,指的是Thread.currentThread()返回的线程。根据JDK API的说法,"该线程不丢失任何监视器的所属权",简单说就是sleep代码上下文如果被加锁了,锁依然在,但是CPU资源会让出给其他线程。
示例:
package main.thread; import java.util.Date; public class ThreadSleepTest { public static void main(String[] args) { SleepThread sleepThread = new SleepThread(); System.out.println("main() before sleepThread.start() " + new Date()); sleepThread.start(); System.out.println("main() after sleepThread.start() " + new Date()); } } class SleepThread extends Thread{ public void run(){ System.out.println("SleepThread run() start, threadName=" + this.getName() + " " + new Date()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("SleepThread run() end, threadName=" + this.getName() + " " + new Date()); } }
执行结果:
1 main() before sleepThread.start() Fri Jun 21 19:16:32 CST 2019 2 main() after sleepThread.start() Fri Jun 21 19:16:32 CST 2019 3 SleepThread run() start, threadName=Thread-0 Fri Jun 21 19:16:32 CST 2019 4 SleepThread run() end, threadName=Thread-0 Fri Jun 21 19:16:34 CST 2019 //与第三行结果差2秒,与run()中的Thread.sleep(2000)是对应的
3、yield()
暂停当前执行的线程对象,并执行其他线程。这个暂停是会放弃CPU资源的,并且放弃CPU的时间不确定,有可能刚放弃,就获得CPU资源了,也有可能放弃好一会儿,才会被CPU执行。
package main.thread; import java.util.Date; public class ThreadYieldTest { public static void main(String[] args) { System.out.println("main() start "+new Date()); YieldThread mt = new YieldThread(); mt.start(); System.out.println("main() end "+new Date()); } } class YieldThread extends Thread{ public void run(){ long beginTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 5000000; i++) { Thread.yield(); count = count + i + 1; } long endTime = System.currentTimeMillis(); System.out.println("用时:" + (endTime - beginTime) + "毫秒!"); } }
执行结果:
//第一次执行结果 main() start Fri Jun 21 19:23:19 CST 2019 main() end Fri Jun 21 19:23:19 CST 2019 用时:1953毫秒! //第二次执行结果 main() start Fri Jun 21 19:24:15 CST 2019 main() end Fri Jun 21 19:24:15 CST 2019 用时:1990毫秒! //第三次执行结果 main() start Fri Jun 21 19:24:35 CST 2019 main() end Fri Jun 21 19:24:35 CST 2019 用时:1984毫秒!
看到,每次执行的用时都不一样,证明了yield()方法放弃CPU的时间并不确定。
4、interrupted()
测试当前线程是否已经中断,执行后具有将状态标识清除为false的功能。换句话说,如果连续两次调用该方法,那么返回的必定是false.
package main.thread; public class ThreadYieldTest { Thread.currentThread().interrupt(); System.out.println("是否停止1?" + Thread.interrupted()); System.out.println("是否停止2?" + Thread.interrupted()); System.out.println("end!"); } }
执行结果:
是否停止1?true 是否停止2?false end!