java Thread 学习

来源:互联网 发布:数控车床梯形螺纹编程 编辑:程序博客网 时间:2024/04/28 07:54
一、Thread and Runnable

之前说过 Thread 的两种实现方式,一种是继承Thread类,一种是实现Runnable接口。 两种实现方式的异同如下:

 

其实就是Class 和 Interface 之间的不同。

Runnable 为一个接口,由于接口的多实现性,使其更具可扩展性,而且有利于资源的共享。比如,一个实现了Runnable接口的对象可以创建多个线程,使他们可以共享该对象的资源。

 

二、线程的五种状态包括

 

新建(new)

就绪(Runnable)

运行(Running)

阻塞(Blocked)

死亡(Dead)

1. 新建状态(New)  : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。

2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

4. 阻塞状态(Blocked)  : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

    (01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。

    (02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。

    (03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5. 死亡状态(Dead)    : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 

图 1线程状态图

 

三、 start 和 run

 

线程中有两种方法:start 和 run

(1)start()方法用来启动一个新线程并且运行,该方法不能被重复调用

(2)Run()方法和普通的成员方法类似,单独调用run()方法会在当前线程中执行run()方法而不会启动一个新线程。

 

四、synchronized

当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。

不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。

synchronized 的三条规则:

第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。

第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

五、线程等待与唤醒

 

这一个有点难理解, 写了几个代码,注释很详细。仔细看一下。

方法简介:(所有方法都是Object的方法)

notify()      

唤醒在此对象监视器上等待的单个线程。注意这里只是进入了就绪状态,并没有running

notifyAll()    

唤醒在此对象监视器上等待的所有线程。注意这里只是进入了就绪状态,并没有running

wait()   

让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)

wait(long timeout)

让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。

 

wait(long timeout, int nanos)

让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)

 (1)wait and notify

 

package waitAndnotify;/** *  * (01) “主线程”通过 new ThreadA("t1") * 新建“线程t1”。随后通过synchronized(t1)获取“t1对象的同步锁”。然后调用t1.start()启动“线程t1”。 (02) * “主线程”执行t1.wait() 释放“t1对象的锁”并且进入“等待(阻塞)状态”。等待t1对象上的线程通过notify() 或 * notifyAll()将其唤醒。 (03) * “线程t1”运行之后,通过synchronized(this)获取“当前对象的锁”;接着调用notify()唤醒 * “当前对象上的等待线程”,也就是唤醒“主线程”。 (04) * “线程t1”运行完毕之后,释放“当前对象的锁”。紧接着,“主线程”获取“t1对象的锁”,然后接着运行。 * */public class WaitClass {public static void main(String[] args) {ThreadA t1 = new ThreadA("t1");synchronized (t1) {// 調用wait()時必須要有該對象的同步鎖/** * (1) * */System.out.println(Thread.currentThread().getName() + "start t1");/** * (2) * */t1.start();//运行t1 但是线程t1 start之后只是进入了就绪状态,并没有获取锁。 所以现在运行的还是主线程/** * (3) * */System.out.println(t1.getName());/** * (4) * */System.out.println(Thread.currentThread().getName() + "wait()");try {/** * Causes the current thread to wait until another thread * invokes the notify() method or the notifyAll() method for * this object. In other words, this method behaves exactly as * if it simply performs the call wait(0). The current thread * must own this object's monitor. The thread releases ownership * of this monitor and waits until another thread notifies * threads waiting on this object's monitor to wake up either * through a call to the notify method or the notifyAll method. * The thread then waits until it can re-obtain ownership of the * monitor and resumes execution. * “当前线程”在调用wait()时,必须拥有该对象的同步锁。该线程调用wait()之后, * 会释放该锁;然后一直等待直到“其它线程”调用对象的同步锁的notify()或notifyAll()方法。 * 然后,该线程继续等待直到它重新获取“该对象的同步锁”,然后就可以接着运行。 */// t1.wait() 是当前线程,即主线程 wait() 而不是t1线程wait()/** * (5) * */t1.wait();//主线程wait 释放锁,讲锁交给t1,开始运行t1} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}/** * (10) *  * *///主线程获得锁System.out.println(Thread.currentThread().getName() + "continue");while (true) {t1.notify();System.out.println("2");try {t1.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}class ThreadA extends Thread {public ThreadA(String name) {super(name);}@Overridepublic void run() {synchronized (this) {/** * (6) * */System.out.println(Thread.currentThread().getName() + "call notify");while (true) {/** * (6) * *///通知wait的线程进入就绪状态,但是此时还没有释放锁this.notify();/** * (7) * */System.out.println("1");try {/** * 8 * */System.out.println(Thread.currentThread().getName() +" wait");//t1 wait 释放锁/** * 9 * */this.wait();Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}

 运行结果:

mainstart t1
t1
mainwait()
t1call notify
1
t1 wait
maincontinue
2
1
t1 wait
2
1
t1 wait
2

(2) wait(long timeout)

package waitAndnotify;public class WaitTimeOut {public static void main(String[] args) {ThreadB b1 = new ThreadB("b1");synchronized (b1) {System.out.println(Thread.currentThread().getName() + " start t1");b1.start();// 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。System.out.println(Thread.currentThread().getName() + " call wait ");try {b1.wait(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + " continue");}}}class ThreadB extends Thread {ThreadB(String name) {super(name);}public void run() {System.out.println(Thread.currentThread().getName() + " run ");while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName());;}}}

运行结果:

main start t1
main call wait 
b1 run 
b1
b1
b1

(3秒后打印continue)
main continue
b1
b1
b1


(3) notifyAll()


public class NotifyAll {private static Object obj = new Object();public static void main(String args[]) {ThreadC c1 = new ThreadC("c1");ThreadC c2 = new ThreadC("c2");ThreadC c3 = new ThreadC("c3");c1.start();c2.start();c3.start();System.out.println("all started");try {Thread.sleep(2000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (obj) {System.out.println("going to notifyall");obj.notifyAll();}}static class ThreadC extends Thread {ThreadC(String name) {super(name);}@Overridepublic void run() {synchronized (obj) {System.out.println(Thread.currentThread().getName() + "wait");try {obj.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "continue");}}}}

运行结果:

all started
c1wait
c2wait
c3wait

两秒后
going to notifyall
c3continue
c2continue
c1continue

为什么wait notify 等方法属于Object?

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。

wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!

OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是:依据“对象的同步锁”。

负责唤醒等待线程的那个线程(我们称为“唤醒线程”),它只有在获取“该对象的同步锁”(这里的同步锁必须和等待线程的同步锁是同一个),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。

总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。


(未完待续。。。)

0 0
原创粉丝点击