之前提到了java线程的启动方式,现在来介绍一下控制线程的工具
package cn.sc.thread;
import java.util.concurrent.TimeUnit;
public class ThreadJoinTest {
public static void main(String[] args) throws InterruptedException {
JoinThread joinThread = new JoinThread();
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + i);
if (1 == i) {
long startTime=System.currentTimeMillis();
joinThread.start();
joinThread.join();
System.out.println(System.currentTimeMillis()-startTime);
}
}
}
}
class JoinThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(currentThread().getName() + i);
if (1 == i) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行后输出
main0
main1
Thread-00
Thread-01
Thread-02
2006
main2
我们可以看到joinThread在调用了join方法之后,main方法被阻塞了,知道joinThread的运行体完全运行完成之后,main方法才继续运行。
准确的说:
当A线程在运行过程中调用了B线程的join,那么A线程会一直阻塞,直到B线程运行完毕,A线程重新进入runnable状态
join的其他几种形式
join():等待被join的线程执行完毕
join(long millis):等待被join的线程的时间最长为millis毫秒。如果在millis毫秒内被join()的线程还没有执行结束,则不再等待。
join(long millis,int nanos):等待被join的线程最长为millis豪秒,nanos豪微秒。
Daemon意为守护神。Daemen是在后台运行的,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,又称为“守护线程”或“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。
后台线程的特征:如果所有的前台线程都死亡了,后台线程也会自动死亡。
package cn.sc.thread;
import java.util.concurrent.TimeUnit;
public class DaemonThreadTest {
public static void main(String[] args) {
DaemonThread daemonThread = new DaemonThread();
daemonThread.setDaemon(true);
long startTime=System.currentTimeMillis();
daemonThread.start();
long endTime = System.currentTimeMillis();
long period=endTime-startTime;
System.out.println("main thread ends "+period);
}
}
class DaemonThread extends Thread{
@Override
public void run() {
System.out.println(currentThread().getName()+" daemon thread");
System.out.println("is daemon thread :"+ isDaemon());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("daemon thread ends");
}
}
运行后,输出
main thread ends 0
Thread-0 daemon thread
is daemon thread :true
通过调用setDaemon(true)方法,我们将daemonThread设置为了后台线程。
main线程默认是前台线程。
前台线程创建中创建的子线程默认是前台线程,后台线程中创建的线程默认是后台线程。
我们可以看到在main方法运行完成之后,daemonThread并没有运行后面的代码,而是直接死亡了。
让当前的正在执行的线程暂停指定的时间,并进入阻塞状态。在其睡眠的时间段内,该线程由于不是处于就绪状态,因此不会得到执行的机会。即使此时系统中没有任何其他可执行的线程,出于sleep()中的线程也不会执行。因此sleep()方法常用来暂停线程执行。
sleep()是一个静态方法,可以通过Thread.sleep(long millis)
直接调用
yield()方法是一个和sleep()方法有点相似的方法,它也是Thread类提供的一个静态方法。它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,只是将该线程转入runnbale就绪状态。yeild()只是让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()线程暂停之后,线程调度器又将其调度出来重新执行。
当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行机会。
package cn.sc.thread;
public class YieldThreadTest {
public static void main(String[] args) {
YieldThread yieldThread1 = new YieldThread();
YieldThread yieldThread2 = new YieldThread();
yieldThread1.setPriority(Thread.MAX_PRIORITY);
yieldThread2.setPriority(Thread.MIN_PRIORITY);
yieldThread1.start();
yieldThread2.start();
}
}
class YieldThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(currentThread().getName()+" "+i);
if(2==i){
Thread.yield();
}
}
}
}
运行之后可以看到“优先度最高”和“优先度最低”线程是交叉执行的,这是因为现在的cpu大多数是多核的原因。如果用户的计算机是单核的,就能看到线程优先级的效果。
yield()方法和sleep()的区别如下:
1. sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()只会给优先级相同,或优先级更高的线程执行机会。
2. sleep()方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态;而yield()不会讲线程转入阻塞状态,它只是将当前线程进入就绪状态。
3. sleep()方法的声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉改异常,要么抛出该异常。
4. sleep()方法比yield()方法具有更好的可移动性,所以建议不要使用yield()方法来控制并发线程的执行。
setPriority用于设置线程的优先级
终止线程有以下两种方法
1. 当此线程线程执行体执行完毕或发生了异常。
2. 使用interrupt中断线程,可以通过isInterrupted来判断是否被中断。如果一个线程在sleep的过程中调用了 interrupt方法会报错java.lang.InterruptedException: sleep interrupted。wait的时候被interrupt会报错 java.lang.IllegalMonitorStateException
3. 使用stop强行终止线程。stop方法已经注解了@Deprecated,且使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不建议使用此方法。
package cn.sc.thread;
public class StopThreadTest {
public static void main(String[] args) {
StopThread stopThread = new StopThread();
stopThread.start();
for (int i = 0; i < 100; i++) {
if(50==i){
stopThread.setStop(true);
}
}
}
}
class StopThread extends Thread{
private boolean stop=false;
public void setStop(boolean stop) {
this.stop = stop;
}
@Override
public void run() {
for (int i = 0; i < 100 & !stop; i++) {
System.out.println(currentThread().getName()+" "+i);
}
}
}
package cn.sc.thread;
public class StopThreadTest {
public static void main(String[] args) {
StopThread stopThread = new StopThread();
stopThread.start();
}
}
class StopThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(currentThread().getName()+" "+i);
if(5==i){
throw new NullPointerException();
}
}
}
}
package cn.sc.thread;
public class InterruptThreadTest {
public static void main(String[] args) throws InterruptedException {
InterruptThread interruptThread = new InterruptThread();
interruptThread.start();
Thread.sleep(1);
interruptThread.interrupt();
}
}
class InterruptThread extends Thread{
@Override
public void run() {
while (!isInterrupted()){
System.out.println("not interrupted");
}
}
}
参考
http://www.cnblogs.com/lwbqqyumidi/p/3804883.html
https://www.cnblogs.com/HDK2016/p/6854683.html#a36