现在的位置: 首页 > 综合 > 正文

Lock与Unlock

2013年03月24日 ⁄ 综合 ⁄ 共 6858字 ⁄ 字号 评论关闭

package test;

public class Tiger3 {
 static class Inner {
  private Thread thread = null;
  private int count = 0;
  public synchronized void getLock() {
   while(tryGetLock() == false) { //无限循环,如果没有其他线程通过释放锁来唤醒它,它永不会退出,因为它无法自己终止while循环。当别的线程调用notify()释放锁后,它会继续执行这次wait()以后的代码,也即会再一次调用tryGetLock()方法来尝试获得锁。

    System.out.println(Thread.currentThread().getName() + "is waited!");
    try {
     wait();
    } catch (InterruptedException e) {
     // TODO: handle exception
    }
    System.out.println(Thread.currentThread().getName() + " is notified!");
   }
  }

/*

    假设在线程一获得锁以后,线程二试图获得锁,这时因为thread 是指向第一个线程的,所以条件一(thread == null)不匹配。这时因为Thread.currentThread()指向的是线程二,而thread 指向的是线程一,所以条件二(thread == Thread.currentThread())也不匹配。然后,getLock()中的wait()方法将会被调用,当前线程(线程二)会暂时陷入wait()状态。直到线程一执行事务完毕后调用freeLock()中的notify()方法释放锁,线程二才被激活。

    所以这个机制的真实意图是通过控制对当前线程句柄的引用来达到调度线程的目的。因此它直接操作的不是帐户或ATM对象,而是当前线程本身。*/

  private synchronized boolean tryGetLock() {
   boolean istrue = false;
   if (thread == null) { //获得对当前线程的一个引用。
    thread = Thread.currentThread();
    istrue = true;
   }
   if (thread == Thread.currentThread()) { //这个条件的设置是为了模拟当前已经获得锁的线程重复尝试获得锁的努力。比如,一个ATM接连重复几次对一个帐户进行存取交易。如果没有设置这个条件,当一个线程第一次调用getLock()获得琐以后马上又再一次调用getLock()试图获得锁,那么wait()方法将会被执行,锁将会被错误地释放。如果所有的线程都如此执行,那么到最后很可能会发生所有线程都在wait()的情形。
    istrue = true;
    count ++;
   }
   return istrue;
  }
  public synchronized void freeLock() {
   if (thread == Thread.currentThread()) {
    count--; // 模拟当前线程在重复尝试获得锁以后重复尝试释放锁。
    if (count == 0) {
     thread = null;
     notify();
     System.out.println(Thread.currentThread().getName() + " call notify()!");
    }
   }
  }
  public synchronized void m() {
   System.out.println(Thread.currentThread().getName() + " begin m()");
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    
   }
   System.out.println(Thread.currentThread().getName() + " end m()");
  }
 }
 public static void main(String[] args) {
  final Inner in = new Inner();
  Thread[] ts = new Thread[10];
  for (int i = 0; i < ts.length; i++) {
   ts[i] = new Thread(
    new Runnable() {
     public void run() {
      System.out.println(Thread.currentThread().getName() + ".start()************");
      in.getLock(); //模拟重复处理事务
      in.m();
      in.getLock(); //模拟重复处理事务
      in.m();
      in.freeLock();
      in.freeLock();

      System.out.println(Thread.currentThread().getName() + ".end()+++++++++++++");
     }
    }, "T" + i
   );
  }
  for (Thread t :ts) {
   t.start();
  }
 }
}

 

结果:

 

T0.start()************
T0 begin m()
T1.start()************
T2.start()************
T3.start()************
T4.start()************
T5.start()************
T6.start()************
T7.start()************
T8.start()************
T9.start()************
T0 end m()
T1is waited!
T2is waited!
T3is waited!
T4is waited!
T5is waited!
T6is waited!
T7is waited!
T8is waited!
T9is waited!
T0 begin m()
T0 end m()
T0 call notify()!
T1 is notified!
T1 begin m()
T0.end()+++++++++++++
T1 end m()
T1 begin m()
T1 end m()
T1 call notify()!
T2 is notified!
T2 begin m()
T1.end()+++++++++++++
T2 end m()
T2 begin m()
T2 end m()
T2 call notify()!
T3 is notified!
T3 begin m()
T2.end()+++++++++++++
T3 end m()
T3 begin m()
T3 end m()
T3 call notify()!
T4 is notified!
T4 begin m()
T3.end()+++++++++++++
T4 end m()
T4 begin m()
T4 end m()
T4 call notify()!
T5 is notified!
T5 begin m()
T4.end()+++++++++++++
T5 end m()
T5 begin m()
T5 end m()
T5 call notify()!
T6 is notified!
T6 begin m()
T5.end()+++++++++++++
T6 end m()
T6 begin m()
T6 end m()
T6 call notify()!
T7 is notified!
T7 begin m()
T6.end()+++++++++++++
T7 end m()
T7 begin m()
T7 end m()
T7 call notify()!
T7.end()+++++++++++++
T8 is notified!
T8 begin m()
T8 end m()
T8 begin m()
T8 end m()
T8 call notify()!
T8.end()+++++++++++++
T9 is notified!
T9 begin m()
T9 end m()
T9 begin m()
T9 end m()
T9 call notify()!
T9.end()+++++++++++++

分析:

十个线程几乎同时被启动,但当线程0获得锁以后,其他九个就马上被阻塞了。直到线程0执行完毕并释放锁,并用notify()方法唤醒九个waiting中的线程中的一个。结果中,Tn.end()++++++++++++行的出现有些错位,这是因为在打印这行内容之前,线程Tn已经释放了锁,而打印这行内容的语句并没有位于synchronized块中,因而是非线程安全的。

 

如果修改代码变成这样:

 

private synchronized boolean tryGetLock() {
   boolean istrue = false;
   if (thread == null) {
    thread = Thread.currentThread();
    istrue = true;
   }
//   if (thread == Thread.currentThread()) {
//    istrue = true;
//    count ++;
//   }

   return istrue;
  }
  public synchronized void freeLock() {
   if (thread == Thread.currentThread()) {
//    count--;
//    if (count == 0) {

     thread = null;
     notify();
     System.out.println(Thread.currentThread().getName() + " call notify()!");
//    }
   }
  }

 

T0.start()************
T0 begin m()
T1.start()************
T2.start()************
T3.start()************
T4.start()************
T5.start()************
T6.start()************
T7.start()************
T8.start()************
T9.start()************
T0 end m()
T1is waited!
T2is waited!
T3is waited!
T4is waited!
T5is waited!
T6is waited!
T7is waited!
T8is waited!
T9is waited!
T0is waited!

分析:因为T0重复调用getLock()却没有释放锁,所以最后导致所有线程都被阻塞,陷入“死锁”状态。

 

 

如果修改代码如下:

 

for (int i = 0; i < ts.length; i++) {
   ts[i] = new Thread(
    new Runnable() {
     public void run() {
      System.out.println(Thread.currentThread().getName() + ".start()************");
      in.getLock();
      in.m1();
      in.m2();
      in.m3();
      in.m4();
      in.freeLock();
      System.out.println(Thread.currentThread().getName() + ".end()+++++++++++++");
     }
    }, "T" + i
   );
  }

其中m1(),m2(),m3(),m4()拷贝自前面的m()方法。

结果:

T0.start()************
T0 begin m1()
T1.start()************
T2.start()************
T3.start()************
T4.start()************
T5.start()************
T6.start()************
T7.start()************
T8.start()************
T9.start()************
T0 end m1()
T1is waited!
T2is waited!
T3is waited!
T4is waited!
T5is waited!
T6is waited!
T7is waited!
T8is waited!
T9is waited!
T0 begin m2()
T0 end m2()
T0 begin m3()
T0 end m3()
T0 begin m4()
T0 end m4()
T0 call notify()!
T1 is notified!
T1 begin m1()
T0.end()+++++++++++++
T1 end m1()
T1 begin m2()
T1 end m2()
T1 begin m3()
T1 end m3()
T1 begin m4()
T1 end m4()

T1 call notify()!
T2 is notified!
T2 begin m1()
T1.end()+++++++++++++
T2 end m1()
T2 begin m2()
T2 end m2()
T2 begin m3()
T2 end m3()
T2 begin m4()
T2 end m4()

T2 call notify()!
T3 is notified!
T3 begin m1()
T2.end()+++++++++++++
T3 end m1()
T3 begin m2()
T3 end m2()
T3 begin m3()
T3 end m3()
T3 begin m4()
T3 end m4()

T3 call notify()!
T4 is notified!
T4 begin m1()
T3.end()+++++++++++++
T4 end m1()
T4 begin m2()
T4 end m2()
T4 begin m3()
T4 end m3()
T4 begin m4()
T4 end m4()

T4 call notify()!
T5 is notified!
T4.end()+++++++++++++
T5 begin m1()
T5 end m1()
T5 begin m2()
T5 end m2()
T5 begin m3()
T5 end m3()
T5 begin m4()
T5 end m4()

T5 call notify()!
T6 is notified!
T6 begin m1()
T5.end()+++++++++++++
T6 end m1()
T6 begin m2()
T6 end m2()
T6 begin m3()
T6 end m3()
T6 begin m4()
T6 end m4()

T6 call notify()!
T7 is notified!
T7 begin m1()
T6.end()+++++++++++++
T7 end m1()
T7 begin m2()
T7 end m2()
T7 begin m3()
T7 end m3()
T7 begin m4()
T7 end m4()

T7 call notify()!
T8 is notified!
T8 begin m1()
T7.end()+++++++++++++
T8 end m1()
T8 begin m2()
T8 end m2()
T8 begin m3()
T8 end m3()
T8 begin m4()
T8 end m4()

T8 call notify()!
T9 is notified!
T9 begin m1()
T8.end()+++++++++++++
T9 end m1()
T9 begin m2()
T9 end m2()
T9 begin m3()
T9 end m3()
T9 begin m4()
T9 end m4()
T9 call notify()!
T9.end()+++++++++++++

分析:

一、可以看得很清楚,在Tn调用四个方法过程中,其它线程是无法接触到这四个方法的。这是单单声明synchronized语句块所不能达到的效果:synchronized可以保护在一个线程执行某一同步块的过程中其它线程无法执行其它同步块,但当该线程执行完这个同步块以后要想继续执行其它同步块时呢?这时候可能其它线程突然跑过来把锁给夺走了。而Lock()和Unlock()机制要解决的正是这个问题。

二、因为Lock()和Unlock()机制控制的不是普通对象,而是线程本身,所以m1(),m2(),m3(),m4()是否是synchronized的就没多大关系了。这是因为任何线程想要执行这四个方法都必须通过getLock()这个方法获得“许可”。而在同一段时间内,永远只有一个线程能获得这种“许可”调用这四个方法。

 

抱歉!评论已关闭.