1. sleep方法
- Thread.sleep(long millis)
值不能为负值,否则会抛 IllegalArgumentException 异常 - Thread.sleep(long millis, int nanos)
可以传入纳米时长,更精准控制时间,nanos取值范围为(0-999999)
- 调用Thread.sleep()方法会使线程调度器把当前线程至于
TIMED_WAITING
状态,一旦时间结束,线程的状态会变回RUNNABLE
并等待CPU执行,因此线程的实际sleep时间会稍大于设定的时间,这取决于操作系统的线程调度器以及系统是否繁忙。 - 调用Thread.sleep()方法总是会暂停当前线程的执行。线程sleep时不会丢失线程已经获取到的监视器锁。
- 任何其它线程都可以打断当前线程的sleep,在这种情况下会抛出 InterruptedException 异常。
2. join方法
- threadInstance.join()
暂停当前线程直到调用join()方法的线程结束,如果该线程被打断,将抛出InterruptedException。 - threadInstance.join(long millis)
millis表示最长等待时间,当前线程最多暂停millis毫秒,或者直到调用该方法的线程结束。由于线程执行取决于操作系统的实现,因此不能保证当前线程的等待时长刚好等于指定时长,通常会稍大于该时长。 - threadInstance.join(long millis, int nanos)
这个方法是为了能更精准地控制等待时长。
写个例子来测试下:
接受一个名字和时长的Thread类
public class MyThread extends Thread{
private String threadName;
private long millis = 1000L;
public MyThread(String name, long millis){
this.threadName = name;
this.millis = millis;
}
@Override
public void run() {
Printer.print(threadName + " started. start time = " + Timer.getTime());
super.run();
try {
sleep(millis);
}catch (Exception e){
e.printStackTrace();
}
Printer.print(threadName + " finished. finish time = " + Timer.getTime());
}
}
然后来测试join方法
public static void main(String[] args){
MyThread thread1 = new MyThread("Thread 1", 2000);
MyThread thread2 = new MyThread("Thread 2", 2000);
MyThread thread3 = new MyThread("Thread 3", 2000);
thread1.start();
try {
thread1.join();
} catch (InterruptedException e){}
thread2.start();
try {
thread2.join(1000);
} catch (InterruptedException e){}
thread3.start();
try {
thread1.join();
thread2.join();
thread3.join();
} catch (InterruptedException e){}
Printer.print("all threads are finished!");
}
根据代码,thread1运行结束之后,thread2开始运行,thread2运行1秒钟之后,thread3开始运行,等三个线程全部运行结束之后,打印出“all threads are finished”。运行程序,输出结果如下:
Thread 1 started. start time = 2019-01-30 10:13:28
Thread 1 finished. finish time = 2019-01-30 10:13:30
Thread 2 started. start time = 2019-01-30 10:13:30
Thread 3 started. start time = 2019-01-30 10:13:31
Thread 2 finished. finish time = 2019-01-30 10:13:32
Thread 3 finished. finish time = 2019-01-30 10:13:33
all threads are finished!
可以看到,输出结果与代码逻辑非常符合。
3. start方法
- threadInstance.start() : 开始运行该线程
有没有想过,如果对同一个线程调用两次start()方法,会怎么样呢?
Thread thread = new MyThread("testThread", 1000);
thread.start();
thread.start();
运行上面这段代码,会抛出如下异常:
Exception in thread "main" java.lang.IllegalThreadStateException
4. run方法
- threadInstance.run()
如果构建Thread实例的时候传入了Runnable对象,此方法将调用Runnable对象的run()方法,否则将什么都不做。
运行如下代码:
Thread thread = new MyThread("testThread", 1000);
thread.run();
Printer.print("print anything");
输出结果:
testThread started. start time = 2019-01-30 11:21:44
testThread finished. finish time = 2019-01-30 11:21:45
print anything
可以看到,直到thread运行结束,打印器才打印了print anything
,可见调用run()方法并没有新开一个线程,而是在当前线程运行的。
我们将thread.run()改为thread.start(),运行代码,输出结果如下:
print anything
testThread started. start time = 2019-01-30 11:24:05
testThread finished. finish time = 2019-01-30 11:24:06
这才是是新开了线程运行的输出结果。
可以得出的结论:Thread调用run()方法只会在当前线程执行,调用start()方法才会在新线程中执行。
5. name和priority的相关方法
- public String getName() : 获取线程的名字
- public void setName(String name) : 设置(更改)线程的名字
- public int getPriority() : 获取线程的优先级
- public void setPriority(int priority) : 设置(更改)线程优先级
6. daemon相关方法
- public void setDaemon(boolean status)
设置线程是否为守护(daemon)线程,这个方法必须在线程开始运行之前调用 - public boolean isDaemon()
获取线程是否为守护线程
通常执行某个具体任务的线程我们称之为使用线程(user thread),而守护线程(daemon thread)则是用来为使用线程服务的,它们默默无闻地在背后运行,执行一些支持任务,比如垃圾回收。
创建一个线程时,新线程会默认继承当前线程的priority
和daemon
状态。
- 当所有的使用线程都执行结束之后(或者说当进程中只剩下守护线程),JVM会关闭它自己
- 如果JVM在关闭之前发现了守护线程,它会先终结守护线程然后关闭自己。但JVM并不关心守护线程有没有在运行
- 守护线程的线程优先级极低