Java Thread的一点知识(II)
?
反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop方法,以后我们再也不要说“停止线程”了。而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果 很难检查出真正的问题所在。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此 时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就 会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级,(b)正在运行的线程因为其它原因而阻塞。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,释放当前线程锁定的任何对象。进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
sleep()方法是本地方法,属于Thread类,它有两种定义:
public static native void sleep(long millis) throws InterruptedException;??
public static void sleep(long millis, int nanos) throws InterruptedException {?
??? //other code?
}?
其中的参数millis代表毫秒数(千分之一秒),nanos代表纳秒数(十亿分之一秒)。这两个方法都可以让调用它的线程沉睡(停止运行)指定的时间,到了这个时间,线程就会自动醒来,变为可运行状态(RUNNABLE),但这并不表示它马上就会被运行,因为线程调度机制恢复线程的运行也需要时间。调用sleep()方法并不会让线程释放它所持有的同步锁;而且在这期间它也不会阻碍其它线程的运行。上面的2个方法都声明抛出一个 InterruptedException类型的异常,这是因为线程在sleep()期间,有可能被持有它的引用的其它线程调用它的 interrupt()方法而中断。中断一个线程会导致一个InterruptedException异常的产生,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
为了更好地理解interrupt()效果,我们来看一下下面这个例子:
public class InterruptTest {?
??? public static void main(String[] args) {?
??????? Thread t = new Thread() {?
??????????? public void run() {?
??????????????? try {?
??????????????????? System.out.println("我被执行了-在sleep()方法前");?
????????????? ??????// 停止运行10分钟?
??????????????????? Thread.sleep(1000 * 60 * 60 * 10);?
??????????????????? System.out.println("我被执行了-在sleep()方法后");?
??????????????? } catch (InterruptedException e) {?
??????????????????? System.out.println("我被执行了-在catch语句块中");?
??????????????? }?
??????????????? System.out.println("我被执行了-在try{}语句块后");?
??????????? }?
??????? };?
??????? // 启动线程?
??????? t.start();?
??????? // 在sleep()结束前中断它?
????? ??t.interrupt();?
??? }?
}?
运行结果:
我被执行了-在sleep()方法前
我被执行了-在catch语句块中
我被执行了-在try{}语句块后
?
wait()方法也是本地方法,属于Object类,有三个定义:
public final void wait() throws InterruptedException {?
??? //do something?
}??
??
1) sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
2) sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
public class ThreadTest implements Runnable{
public void run(){
for(int k=0;k<10;k++){
if(k == 5 && Thread.currentThread().getName().equals("t1")){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+ " : " + k);
}
}
public static void main(String[] args) {
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1");
Thread t2 = new Thread(r,"t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
输出结果:
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7
t1 : 8
t1 : 9
t2 : 0
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9
多次运行这个程序,输出也是一样。这说明:yield()方法不会使不同优先级的线程有执行的机会。
?
?
isInterrupted()和interrputed()方法的区别
isInterrupted方法是实例方法,interrupted方法是静态方法。
Thread.currentThread().isInterrupted()
Thread.interrupted()
首先说明:wait(),notify(),notifyAll()这些方法由java.lang.Object 类提供,而上面讲到的方法都是由java.lang.Thread 类提供(Thread类实现了Runnable 接口)。wait(),notify(),notifyAll()这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized 语句块内使用这三个方法。先看下面了例子:
示例12:
public class ThreadTest implements Runnable{
public static int shareVar = 0;
public synchronized void run(){
if(shareVar == 0){
for(int i=0;i<10;i++){
shareVar++ ;
if(shareVar == 5){
try{
this.wait();
}catch(Exception e){}
}} }
if(shareVar != 0){
System.out.print(Thread.currentThread().getName());
System.out.println(" shareVar = " + shareVar);
this.notify();
}
}
public static void main(String[] args){
Runnable r = new ThreadTest();
Thread t1 = new Thread(r,"t1");
Thread t2 = new Thread(r,"t2");
t1.start();
t2.start();
}}
运行结果:
t2 shareVar = 5
t1 shareVar = 10
t1线程最先执行。由于初始状态下shareVar 为0,t1 将使shareVar 连续加1,当shareVar的值为5 时,t1 调用wait()方法,t1 将处于休息状态,同时释放锁标志。这时t2 得到了锁标志开始执行,shareVar 的值已经变为5,所以t2 直接输出shareVar 的值,然后再调用notify() 方法唤醒t1。t1 接着上次休息前的进度继续执行,把shareVar 的值一直加到10,由于此刻shareVar 的值不为0,所以t1 将输出此刻shareVar的值,然后再调用notify()方法,由于此刻已经没有等待锁标志的线程,所以此调用语句不起任何作用。
?
?
?
?
wait()->本线程进入等待队列,只能通过别的线程的notify()或notifyall()唤醒??
? notify()->随机地从等待队列删除一个线程,也就是说,该线程进入执行状态??
? notifyall()->删除所有在等待队列中的线程,按线程的优先级执行??
? 不推荐使用notify():因为这是随机地从等待队列中取一个线程执行,我们不能通过设定优先级进行控制,如果,随机抽取的线程不能往前执行,这就有可能产生死锁,所以,建议使用notifyall()??
如:
class MyThread extends Thread{
??? Test t = new Test();
??? public void run(){
??? t.test();
??? System.out.println("Thread say:Hello,World!");
??? }
??? public class Test {
??? int x = 0;
??? public void test(){
??? if(x==0)
??? try{ wait();}catch(Exception e){}
??? }
??? }
??? public static void main(String[] args) throws Exception{
??? new MyThread().start();
??? }
}
结果这个线程就不会进入t的wait方法而直接打印出Thread say:Hello,World!.而如果改成:
public class Test {
????? int x = 0;
????? public synchornized void test(){
????? if(x==0)
????? try{
???? ?wait();
????? }catch(Exception e){}
????? }
????? public static void main(String[] args) throws Exception{
????? new MyThread().start();
????? }
}
我们就可以看到线程一直等待,注意这个线程进入等待后没有其它线程唤醒,除非强行退出JVM环境,否则它一直等待.所以请记住:[线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又立即释放该锁]。
public class ThreadA{???
??? public? static void ?main(String[] args){??
????? ThreadB?? b=new?? ThreadB();??
????? b.start(); //这里只是将线程b处于可运行状态,并不会立刻执行,实际上是先执行主线(进)程,再去执行线程b,如果主线程运行时间过长的话,则会在期间执行b。?
????? System.out.println("b?? is?? start....");??
????? synchronized(b){ //定义一个同步块,使用b作为资源锁。对于wait方法必须有的?????
??????? try{??
????????? System.out.println("Waiting?? for?? b?? to?? complete...");??
????????? b.wait();//临时释放锁,并阻塞当前线程。让其他使用同一把锁的线程ThreadB有机会执行。 注意:直接写wait()是不对的。?
????????? System.out.println("waiting....");??
??????? }catch(InterruptedException e){}??
???? }??
????? System.out.println("Total?? is?? :"+b.total);
???? }???
? }??
? class? ThreadB extends Thread{??
? int?? total;??
? public?? void?? run() {??
????? System.out.println("ThreadB?? is?? running?? ....");??
???? ?synchronized(this){ //必须有
????? System.out.println("ThreadB?? is?? excuting?? for?? statement..");??
????? for(int i=0;i<5;i++){??
???????? total+=i;??
???????? System.out.println("total?? is?? "+total);??
????? }??
???? ?notify();? //在已经获得了一个对象的锁的前提下,调用notify()会通知线程调度程序,唤醒其它等待这个锁的线程队列中的线程,notifyAll()唤醒所有这类线程。
??? }?? //end for synchronized
? }?
? }
运行结果:
b?? is?? start....
Waiting?? for?? b?? to?? complete...
ThreadB?? is?? running?? ....
ThreadB?? is?? excuting?? for?? statement..
total?? is?? 0
total?? is?? 1
total?? is?? 3
total?? is?? 6
total?? is?? 10
waiting....
Total?? is?? :10
?
程序中必须同时满足以下四个条件才会引发死锁:
互斥(Mutual exclusion):线程所使用的资源中至少有一个是不能共享的,它在同一时刻只能由一个线程使用。
持有与等待(Hold and wait):至少有一个线程已经持有了资源,并且正在等待获取其他的线程所持有的资源。
非抢占式(No pre-emption):如果一个线程已经持有了某个资源,那么在这个线程释放这个资源之前,别的线程不能把它抢夺过去使用。
循环等待(Circular wait):假设有N个线程在运行,第一个线程持有了一个资源,并且正在等待获取第二个线程持有的资源,而第二个线程正在等待获取第三个线程持有的资源,依此类推……第N个线程正在等待获取第一个线程持有的资源,由此形成一个循环等待。
举例:
1.未发生死锁
public class Test {???
?? static Object o1 = new Object();//静态对象
?? static Object o2 = new Object();???
??? public static void main(String[] args) {????????
???????? synch synch1 = new synch();
???????? synch synch2 = new synch();
?????? ??synch1.a=0;
???????? synch2.a=2;
???????? Thread t1 = new Thread(synch1);
???????? Thread t2 = new Thread(synch2);
???????? t1.start();
???????? t2.start();
??? }??
}//Test类结束
class synch implements Runnable{???
??? int a;
??? public void run() {
???? ???System.out.println(a);
??????? if(a == 0){
????????? try {
??????????? synchronized (Test.o1) {//对类Testt中的静态对象同步???????????????
??????????????? Thread.sleep(500);
??????????? } //synchronized是对o1的对象锁,500毫秒以后会释放掉对o1的锁,所以不会发生死锁 ????????
??????????? synchronized (Test.o2) {
??????????????? System.out.println("aaaa");
??????????? }
??????? } catch (InterruptedException ex) {}
?????? }else
?????????? try {
??????????????? synchronized (Test.o2) {
?????????????????? Thread.sleep(500);
??????????? }?????????? ?
??????????? synchronized (Test.o1) {
??????????????? System.out.println("aaba");
??????????? }
??????? } catch (InterruptedException ex) {}
???????
??? }
}
运行后显示
0
2
aaba
aaaa
没有发生死锁
2.发生了死锁
修改如下:
public void run() {
??? System.out.println(a);
??? if(a == 0){
??????? try {
??????????? synchronized (Test.o1) { //已获取o1对象锁
??????????????? Thread.sleep(500);
??????????????? System.out.println("Try to get o2");
??????????????? synchronized (Test.o2) {? //未释放o1(同步块中的语句执行完以后才释放该对象的锁),尝试获取o2,被挂起
?????????? ?????????System.out.println("aaaa");
??????????????? }
??????????? }
??????? } catch (InterruptedException ex) {}
??? } else {
??????? try {
??????????? synchronized (Test.o2) { //已获取o2的对象锁
??????????????? Thread.sleep(500);
??????????????? System.out.println("Try to get o1");
??????????????? synchronized (Test.o1) {? //未释放o2,尝试获取o1,也被挂起,死锁
??????????????????? System.out.println("aaba");
??????????????? }
??????????? }
??????? } catch (InterruptedException ex) {}
??? }
}
结果:
0
2
Try to get o2
Try to get o1
另一个例子:
class Friendly {
??? private Friendly partner;
??? private String name;
??? public Friendly(String name) {
??????? this.name = name;
??? }
??? public synchronized void hug() {
??????? System.out.println(Thread.currentThread().getName()+" in " + name + ".hug() trying to invoke " + partner.name +".hugBack()");
??????? partner.hugBack();
??? }
??? private synchronized void hugBack() {
??????? System.out.println(Thread.currentThread().getName()+" in " + name + ".hugBack()");
??? }
??? public void becomeFriend(Friendly partner) {
??????? this.partner = partner;
??? }
public static void main(String[] args) {
??? final Friendly jareth = new Friendly("jareth");
??? final Friendly cory = new Friendly("cory");
??? jareth.becomeFriend(cory);
??? cory.becomeFriend(jareth);
??? new Thread(new Runnable() {
??????? public void run() { jareth.hug(); }
??? }, "Thread1").start();
?
??? new Thread(new Runnable() {
??????? public void run() { cory.hug(); }
??? }, "Thread2").start();
}
}
结果:
Thread1 in jareth.hug() trying to invoke cory.hugBack()
Thread1 in cory.hugBack()
Thread2 in cory.hug() trying to invoke jareth.hugBack()
Thread2 in jareth.hugBack()