一、后台线程(守护线程)
守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如JVM垃圾回收线程就是一个典型的守护线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序就没运行的必要了,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
调用Thread对象的setDaemon(true)方法可以将该线程设置成后台线程。该方法必须在启动线程前调用。
还有一个方法isDaemon()方法可以测试该线程是否为守护线程。
public class DaemonThread extends Thread {
public static void main(String[] args) {
DaemonThread thread = new DaemonThread();
// 将此线程设置成后台线程
thread.setDaemon(true);
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + i);
}
}
}
运行结果:
... Thread-0 10 main 9 Thread-0 11 ... Thread-0 32 Thread-0 33 ...
可以看到当主线程运行完后,守护线程无法运行到循环变量100,可以判断出当非守护线程全部结束时,守护线程也会结束。
二、线程睡眠
通过调用Thread类的静态方法sleep(),可以在指定的毫秒数内让当前正在执行的线程阻塞(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
public class SleepTest {
public static void main(String[] args) throws Exception {
for(int i =0;i<10;i++) {
System.out.println("当前时间:"+new java.util.Date());
Thread.sleep(1000);
}
}
}
运行结果:
当前时间:Fri Sep 29 15:00:16 CST 2017 当前时间:Fri Sep 29 15:00:17 CST 2017 当前时间:Fri Sep 29 15:00:18 CST 2017
运行上面程序可以看到程序输出一次当前时间后,程序暂停一秒,然后再输出一次。
三、join线程
当在某个线程中调用其他线程的join()方法时,调用的线程会被阻塞,直到join线程执行完为止。
public class JoinThread extends Thread {
public JoinThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
if (i == 3) {
JoinThread jt = new JoinThread("执行join的线程");
jt.start();
jt.join();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
运行结果:
main 0 main 1 main 2 执行join的线程 0 执行join的线程 1 main 3 ...
可以看到调用join()方法的主线程被阻塞,知道join线程执行完才开始执行下面的代码。
四、线程的优先级
每个线程执行时都具有一定的优先级,优先级高的线程会获得更多的执行机会,而优先级低的则较少。
每个线程默认的优先级与创建它的父线程的优先级相同,在默认情况下,main线程具有普通优先级(NORM_PRIORITY
)。
Thread类提供了setPriority(int newPriority)、getPriority()方法来设置和返回指定线程的优先级,其中setPriority()方法的参数是一个整数,范围是1~10,也可以是下面的镜头常量。
Thread类有如下三个静态常量:
- NORM_PRIORITY:其值是5
- MIN_PRIORITY:其值是1
- MAX_PRIORITY:其值是10
public class PriorityTest extends Thread {
public PriorityTest(String name) {
super(name);
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + ",其优先级是:" + getPriority() + ",循环变量的值为:" + i);
}
}
public static void main(String[] args) {
System.out.println("主线程的优先级:" + Thread.currentThread().getPriority());
PriorityTest low = new PriorityTest("低优先级线程");
PriorityTest high = new PriorityTest("高优先级线程");
System.out.println(low.getName()+"创建之前的的优先级:"+low.getPriority());
System.out.println(high.getName()+"创建之前的的优先级:"+high.getPriority());
low.start();
low.setPriority(MIN_PRIORITY);
high.start();
high.setPriority(MAX_PRIORITY);
}
}
运行几次看到的结果都是high线程优先执行完的。
五、线程让步
yield()方法与sleep()方法相似,同样是Thread类的静态方法,也可以让当前线程暂停,但它不会阻塞该线程,而是将该线程转入就绪状态。yield()只是让当前线程暂停一下,让系统重新调度一次,所以很有可能重新执行原来的线程,而且该方法受优先级影响。
public class YieldTest extends Thread {
@Override
public void run() {
for(int i=0;i<20;i++) {
System.out.println(getName() + " " + i);
if(i==5) {
Thread.yield();
}
}
}
public static void main(String[] args) {
YieldTest yt1 = new YieldTest();
YieldTest yt2 = new YieldTest();
yt1.start();
yt2.start();
}
}
运行结果:
...
Thread-1 4
Thread-0 5
Thread-1 5
Thread-0 6
...
程序开启两条优先级一样的线程,两个线程的循环变量i执行到5的时候,会发现线程会切换给另一个线程执行。