Java提供线程控制的工具方法。
16.4.1 join线程
Thread提供了让一个线程等待另外一个线程完成的方法join()方法。当在某个线程执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。
PS:join方法通常使用线程的程序调用,以将大问题划分成许多小问题,每个小问题分配一个线程。当所有小线程都完成后,再调用主线程来操作。
package com.cdmt.collection.list;
public class JoinThread extends Thread {
//提供一个带参构造器
public JoinThread(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//获取当前线程
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动子线程
new JoinThread("新线程").start();
for(int i = 0; i<100; i++){
if(i == 20){
JoinThread jt = new JoinThread("被Join的线程");
jt.start();
//main线程调用了jt的join方法,main线程必须等待jt执行完毕
jt.join();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
结果:
总结:
共3线程,主方法开始时启动新线程,该线程将会和main线程并发执行。当主线程i变为20时,启动被Join的线程,main线程必须等待Join完成才可以执行。在被Join的线程执行时,实际上只有2个子线程并发执行,main线程处于等待状态。
join()方法有三种重载形式:
有一种线程在后天运行,它的任务就是为其他线程提供服务,这种线程被称为后天线程(Daemon Thread),又被称为守护线程或者精灵线程。JVM的垃圾回收线程就是一个典型的后台线程。
后台线程有个特征:如果所有的前台程序都死亡,后台线程会自动死亡。
调用Thread对象的setDaemon(true)方法可以将指定线程设置成后台线程。当前台线程都死亡时,后台线程随之死亡。
package com.cdmt.collection.list;
public class DaemonThread extends Thread {
//定义后台线程的线程执行体与普通线程没有任何区别
@Override
public void run() {
for(int i=0; i<1000;i++){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
DaemonThread dt = new DaemonThread();
//将此线程设置成后台线程
dt.setDaemon(true);
//启动后台线程
dt.start();
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
//程序执行到此处,前台线程main结束
//后台线程也应该随之结束
}
}
结果:
总结:
本来子线程要到999才会结束,但是由于前天线程全部死了,所以后台线程也就被JVM杀死。
PS:前台线程死亡后JVM通知后台线程死亡,但从它接收指令到做出响应,需要一定时间而且要将某个线程设直为后台线程,必须在该线程启动之前设置,也就是说setDaemon( true) 须在 start() 方法之前调用,否则会引发IllegalThreadStateException异常。
如果需要让当前执行的线程 暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法实现。
sleep有2种重载形式:
与前面类似的是,程序很少调用第二种形式的sleep方法。
当前线程调用sleep方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于sleep方法中的线程也不会执行,因此sleep方法常用来暂停程序的执行。
package com.cdmt.collection.list;
import java.util.Date;
public class SleepTest extends Thread {
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<10;i++){
System.out.println("当前时间是: " + new Date());
Thread.sleep(2000);
}
}
}
结果:
总结:
系统完全让出资源,每2s之后继续调用执行。yeild()方法只是让线程进入就绪状态,优先级比当前线程大或者相等的才可以得到资源。否则线程会继续执行。
每个线程都有优先级,优先级高的线程获得执行机会比较多。每个线程默认的优先级都与创建它的父线程的优先级相同,在默认情况下,main线程具有普通优先级,由main线程创建的子线程也具有普通优先级。
Thread提供了setPriority(int newPriority)、getPriority()方法来设置和返回指定线程的优先级,其中setPriority()方法的参数可以是一个整数,范围是1-10之间,也可以使用Thread类如下的三个静态常量。
下面程序使用了setPriority方法来改变主线程的优先级
package com.cdmt.collection.list;
public class PriorityTest extends Thread {
//定义一个有参数的构造器,用于创建线程时指定name
public PriorityTest(String name){
super(name);
}
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println(getName() + ",其优先级是: " + getPriority() + ",循环变量的值为:" + i);
}
}
public static void main(String[] args) {
Thread.currentThread().setPriority(6);
for(int i=0; i<30; i++){
if(i==10){
PriorityTest low = new PriorityTest("低级");
low.start();
System.out.println("创建之初的优先级: " + low.getPriority());
low.setPriority(Thread.MIN_PRIORITY);
}
if(i==20){
PriorityTest high = new PriorityTest("高级");
high.start();
System.out.println("创建之初的优先级: " + high.getPriority());
high.setPriority(Thread.MAX_PRIORITY);
}
}
}
}
结果:
总结:
设置高的优先级有限运行。