在学习java中对线程的支持之前,先了解线程的几种状态。
线程的几种状态
线程从创建到死亡会经历很多的状态:创建,(就绪,执行),阻塞,waiting,time_waiting,死亡。waiting和time_wait属于block
Thread内部类,线程的状态1
2
3
4
5
6
7
8
9
10
11
12
13
14public enum State {
//此时还没有调用start()
NEW,
//调用了start(),包含就绪和执行状态
RUNNABLE,
//阻塞状态:线程等待一个锁,当要进入synchronized代码块或者lock时可能进入这个状态
BLOCKED,
//线程正在等该什么,当调用wait(),join(),等方法会进入这个状态
WAITING,
//带有时间限制的等待,sleep(),wait(time),join(time)
TIMED_WAITING,
//终止状态,线程已完成任务
TERMINATED;
}
当需要一个线程时,我们创建一个线程,但是此时线程还无法工作,因为它可能需要一些资源例如文件,IO等;
当获取到除了cpu之外的资源时它进入到了就绪状态,只要获得cpu就能够执行;
当获取到cpu,线程进入执行状态;
等到cpu时间片结束就又进入了就绪状态;
但是如果在执行期间线程已经完成了工作,那么就直接死亡;
在执行期间可能会主动进入waiting状态,此时它会释放锁和资源;
在执行期间也可能进入sleep状态,但是不会释放自己所获得的资源(包括锁);
如果在执行期间被同步代码块(获取不到锁)或者获取不到IO资源等会进入阻塞状态,此时他不会释放自己所获得的资源,例如两个线程需要a,b两个锁,线程x获得了a,线程Y获得了b,此时X想要获得b,Y想要获得a;那么它们都进入了阻塞状态,由于不释放资源,它们会死锁;
上下文切换
一颗cpu同一时刻只能被一个线程占有,那么cpu切换线程就意味着需要保存此时线程所有的状态,保证下一次执行时能够从现在的状态恢复。线程的切换代价较大,但是进程的切换代价更大。
Thread类中的方法
currentThread()方法可以获得当前的线程对象
start()方法用来启动一个线程
run(),注意run方法不是用户调用的,它不会创建一个线程
sleep(long time)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。也就是不释放锁。
1 | public class Study2 extends Thread { |
输出结果:1
2
3
4
5
6
7
8now thresd: 12 try to enter synchronized
now thresd: 11 try to enter synchronized
now thread: 12 is run and sleep
now thread: 12 exit synchronized
now thread: 11 is run and sleep
now thread: 11 exit synchronized
Process finished with exit code 0
可以看见当一个线程sleep之后不释放资源,而且时间到了就会醒来继续执行,执行完才释放锁
- join(long millis):thread.join()方法会使调用join方法的线程先执行完或者执行一定的时间,再执行之后的数据,如果没有传入参数,会等待该线程结束。
接着上一个例子,将main方法改变为以下代码1
2
3
4
5
6
7
8
9
10public static void main(String[] args) throws InterruptedException {
Study2 study=new Study2();
Study2 study1=new Study2();
study.start();
study.join();
System.out.println("wait "+study.getId()+" finish");
study1.start();
}
此时控制台输出:如下:永远都是study执行完了才开始执行接下来的内容1
2
3
4
5
6
7now thresd: 11 try to enter synchronized
now thread: 11 is run and sleep
now thread: 11 exit synchronized
wait 11 finish
now thresd: 12 try to enter synchronized
now thread: 12 is run and sleep
now thread: 12 exit synchronized
yield()方法暂停当前正在执行的线程对象,并执行其他线程,也就是交出cpu执行权,让线程重回就绪状态。调度程序可以忽略此方法。
interrupt()方法可以中断一个阻塞的线程
注意sleep(time),yield()不释放锁
关于一些获取属性的方法,以get开头的方法,比如getId,getName等。
Thread类源码分析
成员变量
比较常见的有:
1 | //线程的名字 |
成员方法
- start()
1 |
|
2.sleep(long millis)是一个native方法,注意调用该方法不会失去monitor的所有权
1 | public static native void sleep(long millis) throws InterruptedException; |
- join()方法
1 | public final synchronized void join(long millis) |
- yield()也是一个native方法
1 | public static native void yield(); |