Java的线程支持提供了一些便捷的工具方法,通过这些便捷的工具方法可以很好地控制线程的执行。
join线程
Thread提供了让一个线程等待另一个线程完成的方法——join()方法。在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,知道被join()方法加入的join线程执行完为止。
join()方法通常使用线程的程序调用,以将大问题划分为许多小问题,每个小问题分配一个线程,当所有的小问题都得到处理后,再调用主线程来进一步操作。
代码实现
public class JoinThread extends Thread{
//提供一个有参数的构造器,用于设置该线程的名字
public JoinThread(String name){
super(name);
}
//重写run()方法,定义线程执行体
public void run(){
for(int i = 0; i < 10; i++){
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws Exception{
//启动子线程
new JoinThread("新线程").start();
for(int i = 0; i < 10; i++){
if(i == 2){
JoinThread jt = new JoinThread("被join的线程");
jt.start();
//main线程调用了jt线程的join()方法,main线程
//必须等jt执行结束才回向下执行
jt.join();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
代码结果
main 0
main 1
新线程 0
新线程 1
新线程 2
新线程 3
新线程 4
新线程 5
新线程 6
新线程 7
新线程 8
新线程 9
被join的线程 0
被join的线程 1
被join的线程 2
被join的线程 3
被join的线程 4
被join的线程 5
被join的线程 6
被join的线程 7
被join的线程 8
被join的线程 9
main 2
main 3
main 4
main 5
main 6
main 7
main 8
main 9
可以看出,上面程序共有三个线程,主方法开始时就启动了“新线程”的子线程,该子线程将会和main线程并发执行。当主线程的循环变量等于2时,就启动了“被join的线程”,该线程不会和main线程并发执行,main线程必须等待该线程执行结束后才可以向下执行。
join方法常用的重载形式:
-join():等待被join的线程执行完。
-join(long millis):等待被join的线程的时间最长为millis毫秒。
后台线程
后台线程--在后台运行,为其他线程提供服务,或称为“守护线程”、“精灵线程”。JVM的垃圾回收线程就是后台线程。
后台线程有个最重要的特征:如果所有的前台线程都死亡,后台线程会自动死亡。
调用Thread对象的setDaemon(true)方法可将制定线程设置成后台线程,此外还提供了一个isDaemon()方法,用于判断指定线程是否为后台线程。
代码
public class DaemonThread extends Thread{
public DaemonThread(String name){
super(name);
}
//定义后台线程的线程执行体与普通线程没有任何区别
public void run(){
for(int i = 0; i < 100; i++){
System.out.println(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线程)结束
//后台线程也随之结束
}
}
其中需要注意的是,setDaemon(true)方法必须在start()方法之前调用,否则会引发异常。
线程睡眠
通过调用sleep()方法使正在执行的线程暂停一段时间,并进入阻塞状态。
当当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于sleep()中的线程也不会执行,一次sleep()方法常用来暂停程序的执行。
代码
public class SleepThread {
public static void main(String[] args)throws Exception{
for(int i = 0; i < 10; i++){
System.out.println("当前时间:" + new Date());
Thread.sleep(1000);
}
}
}
执行结果
当前时间:Mon Oct 31 17:25:25 CST 2016
当前时间:Mon Oct 31 17:25:26 CST 2016
当前时间:Mon Oct 31 17:25:27 CST 2016
当前时间:Mon Oct 31 17:25:28 CST 2016
当前时间:Mon Oct 31 17:25:29 CST 2016
当前时间:Mon Oct 31 17:25:30 CST 2016
当前时间:Mon Oct 31 17:25:31 CST 2016
当前时间:Mon Oct 31 17:25:32 CST 2016
当前时间:Mon Oct 31 17:25:33 CST 2016
当前时间:Mon Oct 31 17:25:34 CST 2016
线程让步:yield
yield()方法类似于sleep()方法,都是让当前正在执行的线程暂停。不同的是它不会阻塞该线程,它只是将该线程转入就绪状态。
当某个线程调用yield()方法暂停后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才回获得执行的机会。
代码
public class YieldTest extends Thread{
public YieldTest(String name){
super(name);
}
//定义run()方法
public void run(){
for(int i = 0; i < 50; i++){
System.out.println(getName() + " " + i);
//当i等于20时,使用yield()方法让当前线程让步
if(i == 20){
Thread.yield();
}
}
}
public static void main(String[] args){
//启动两个并发线程
YieldTest yt1 = new YieldTest("高级");
//将yt1线程设置成最高优先级
yt1.setPriority(MAX_PRIORITY);
yt1.start();
YieldTest yt2 = new YieldTest("低级");
yt2.setPriority(MIN_PRIORITY);
yt2.start();
}
}
sleep()方法与yield()方法的区别:
--sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会。
--sleep()方法会将线程转入阻塞状态,知道经过阻塞时间才会转入就绪状态;而yield()方法不会将线程转入阻塞状态它只是墙纸当前线程进入就绪状态。因此完全有可能某个线程在调用yield()方法暂停后,立即在此获得处理器资源被执行。
--sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常。
--sleep()方法比yield()方法有更好的可移植性,通常不太使用yield()方法来控制并发线程的执行
改变线程优先级
每个线程执行时都具有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较少的执行机会。
每个线程的默认优先级都与创建它的父线程的优先级相同,在默认情况下,main线程具有普通优先级,由main线程创建的子线程也具有普通优先级。
Thread类提供了setPriority(int newPriority)、getPriority()方法来设置和返回指定线程的优先级。其中setPriority()方法的参数可以是一个整数,范围在1~10之间,也可以使用Thread类的三个静态常量。
--MAX_PRIORITY: 其值是10。
--MIN_PRIORITY: 其值是1。
--NORM_PRIORITY: 其值是。
代码
public class PriorityTest extends Thread{
public PriorityTest(String name){
super(name);
}
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(MIN_PRIORITY);
}
if(i == 20){
PriorityTest high = new PriorityTest("高级");
high.start();
System.out.println("创建之初的优先级:" + high.getPriority());
//设置该线程为最高优先级
high.setPriority(MAX_PRIORITY);
}
}
}
}