黑马程序员技术交流社区

标题: [深圳特色][问答网]理解线程同步“锁” [打印本页]

作者: 深圳黑马程序员    时间: 2015-11-12 15:56
标题: [深圳特色][问答网]理解线程同步“锁”
本帖最后由 xiaoqing 于 2015-11-12 20:03 编辑

         深圳问答网又公布新一期的技术问答啦。
         这次的问题有点专业哦,要理解同步,那么关于“锁”这个概念一定要理解。不然写多线程的时候,就会出大问题啦。很多莫名其妙的bug就是由于没有理解好线程和锁的概念而导致的哦。来看看问答网中关于这个概念的回答吧。
         
-------------------------------------------------------华丽的分割线------------------------------------------------------
      Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
     一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
     二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
     三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
     四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
     五、以上规则对其它对象锁同样适用.
举例说明:  
     一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
  1. public class Thread1 implements Runnable {  

  2.      public void run() {  

  3.           synchronized(this) {  

  4.                for (int i = 0; i < 5; i++) {  

  5.                     System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);  

  6.                }  

  7.           }  

  8.      }  

  9.      public static void main(String[] args) {  

  10.           Thread1 t1 = new Thread1();  

  11.           Thread ta = new Thread(t1, "A");  

  12.           Thread tb = new Thread(t1, "B");  

  13.           ta.start();  

  14.           tb.start();  

  15.      }     

  16. }
复制代码
结果:  
     A synchronized loop 0  
     A synchronized loop 1  
     A synchronized loop 2  
     A synchronized loop 3  
     A synchronized loop 4  
     B synchronized loop 0  
     B synchronized loop 1  
     B synchronized loop 2  
     B synchronized loop 3  
     B synchronized loop 4
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
  1. public class Thread2 {  

  2.      public void m4t1() {  

  3.           synchronized(this) {  

  4.                int i = 5;  

  5.                while( i-- > 0) {  

  6.                     System.out.println(Thread.currentThread().getName() + " : " + i);  

  7.                     try {  

  8.                          Thread.sleep(500);  

  9.                     } catch (InterruptedException ie) {  

  10.                     }  

  11.                }  

  12.           }  

  13.      }  

  14.      public void m4t2() {  

  15.           int i = 5;  

  16.           while( i-- > 0) {  

  17.                System.out.println(Thread.currentThread().getName() + " : " + i);  

  18.                try {  

  19.                     Thread.sleep(500);  

  20.                } catch (InterruptedException ie) {  

  21.                }  

  22.           }  

  23.      }  

  24.      public static void main(String[] args) {  

  25.           final Thread2 myt2 = new Thread2();  

  26.           Thread t1 = new Thread(  new Runnable() {  public void run() {  myt2.m4t1();  }  }, "t1"  );  

  27.           Thread t2 = new Thread(  new Runnable() {  public void run() { myt2.m4t2();   }  }, "t2"  );  

  28.           t1.start();  

  29.           t2.start();  

  30.      }     

  31. }
复制代码
结果:  
     t1 : 4  
     t2 : 4  
     t1 : 3  
     t2 : 3  
     t1 : 2  
     t2 : 2  
     t1 : 1  
     t2 : 1  
     t1 : 0  
     t2 : 0
     三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
  1. //修改Thread2.m4t2()方法:  

  2.      public void m4t2() {  

  3.           synchronized(this) {  

  4.                int i = 5;  

  5.                while( i-- > 0) {  

  6.                     System.out.println(Thread.currentThread().getName() + " : " + i);  

  7.                     try {  

  8.                          Thread.sleep(500);  

  9.                     } catch (InterruptedException ie) {  

  10.                     }  

  11.                }  

  12.           }
  13.      }
复制代码
结果:
     t1 : 4  
     t1 : 3  
     t1 : 2  
     t1 : 1  
     t1 : 0  
     t2 : 4  
     t2 : 3  
     t2 : 2  
     t2 : 1  
     t2 : 0
     四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
  1. //修改Thread2.m4t2()方法如下:
  2.      public synchronized void m4t2() {  
  3.           int i = 5;  
  4.           while( i-- > 0) {  
  5.                System.out.println(Thread.currentThread().getName() + " : " + i);  
  6.                try {  
  7.                     Thread.sleep(500);  
  8.                } catch (InterruptedException ie) {  
  9.                }  
  10.           }  
  11.      }
复制代码
结果:  
     t1 : 4  
     t1 : 3  
     t1 : 2  
     t1 : 1  
     t1 : 0  
     t2 : 4  
     t2 : 3  
     t2 : 2  
     t2 : 1  
     t2 : 0
     五、以上规则对其它对象锁同样适用:
  1. public class Thread3 {   

  2.      class Inner {     

  3.           private void m4t1() {     

  4.                int i = 5;     

  5.                while(i-- > 0) {     

  6.                     System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);     

  7.                     try {     

  8.                          Thread.sleep(500);     

  9.                     } catch(InterruptedException ie) {     

  10.                     }     

  11.                }     

  12.           }     

  13.           private void m4t2() {     

  14.                int i = 5;     

  15.                while(i-- > 0) {     

  16.                     System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);     

  17.                     try {     

  18.                          Thread.sleep(500);     

  19.                     } catch(InterruptedException ie) {     

  20.                     }     

  21.                }     

  22.           }     

  23.      }     

  24.      private void m4t1(Inner inner) {     

  25.           synchronized(inner) { //使用对象锁     

  26.           inner.m4t1();     

  27.      }     

  28.      private void m4t2(Inner inner) {     

  29.           inner.m4t2();     

  30.      }     

  31.      public static void main(String[] args) {     

  32.           final Thread3 myt3 = new Thread3();     

  33.           final Inner inner = myt3.new Inner();     

  34.           Thread t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1");     

  35.      Thread t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2");     

  36.      t1.start();     

  37.      t2.start();     

  38.   }     

  39. }
复制代码
结果:
尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。
     t1 : Inner.m4t1()=4  
     t2 : Inner.m4t2()=4  
     t1 : Inner.m4t1()=3  
     t2 : Inner.m4t2()=3  
     t1 : Inner.m4t1()=2  
     t2 : Inner.m4t2()=2  
     t1 : Inner.m4t1()=1  
     t2 : Inner.m4t2()=1  
     t1 : Inner.m4t1()=0  
     t2 : Inner.m4t2()=0
现在在Inner.m4t2()前面加上synchronized:
  1. private synchronized void m4t2() {  

  2.           int i = 5;  

  3.           while(i-- > 0) {  

  4.                System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);  

  5.                try {  

  6.                     Thread.sleep(500);  

  7.                } catch(InterruptedException ie) {  

  8.                }  

  9.           }  

  10.      }
复制代码
结果:
尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2对Inner.m4t2()的访问也被阻塞,因为m4t2()是Inner中的一个同步方法。

     t1 : Inner.m4t1()=4  
     t1 : Inner.m4t1()=3  
     t1 : Inner.m4t1()=2  
     t1 : Inner.m4t1()=1  
     t1 : Inner.m4t1()=0  
     t2 : Inner.m4t2()=4  
     t2 : Inner.m4t2()=3  
     t2 : Inner.m4t2()=2  
     t2 : Inner.m4t2()=1  
     t2 : Inner.m4t2()=0
-------------------------------------------------------华丽的分割线------------------------------------------------------

      看完上面的问答,再自己做个Demo跑一遍,这样才能更好的理解Java中关于同步的概念。理解了锁,才能处理好多线程同步问题。

[深圳特色] 揭秘内部问答网中经典问题,每周更新



作者: oup    时间: 2015-11-13 22:37
好没学到多线程 ,以后学到了过来看!
作者: 年强    时间: 2015-11-14 11:11
我表示很疑惑。。。我写的时候可以做到同步啊。。。但我没考虑这样的问题




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2