`

线程中的Synchronized

 
阅读更多

1、synchronized关键字的作用域有二种:
  1)是某个对象实例内,synchronized aMethod(){} 可以防止多个线程同时 访问这个对象的synchronized方法 (如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法 )。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
      总的来说,这种情况,锁就是这个方法所在的对象

  2)是某个类的范围,synchronized static aStaticMethod{} 防止多个线程同时访问这个类中的synchronized static 方法 。它可以对类的所有对象实例起作用。此时锁就是这个class

2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
   用法是: synchronized(this){/*区块*/},锁就是这个方法所在的对象 ;

3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
 
4、无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
   每个对象只有一个锁(lock)与之相关联。
   实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

1、理论上,每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的 。假如有这样的代码:

Java代码 复制代码 收藏代码
  1. public class ThreadTest{     
  2.   public void test(){     
  3.      Object lock=new Object();     
  4.      synchronized (lock){     
  5.         //do something     
  6.      }     
  7.   }     
  8. }   
 public class ThreadTest{  
   public void test(){  
      Object lock=new Object();  
      synchronized (lock){  
         //do something  
      }  
   }  
 } 

 lock变量作为一个锁存在根本没有意义,因为它根本不是共享对象,每个线程进来都会执行Object lock=new Object();每个线程都有自己的lock,根本不存在锁竞争。

2、为了保证银行账户的安全,可以操作账户的方法如下:

Java代码
public synchronized void add(int num) {     
  1.       balance = balance + num;     
  2.  }     
  3.  public synchronized void withdraw(int num) {     
  4.       balance = balance - num;     
  5.  }    
public synchronized void add(int num) {  
      balance = balance + num;  
 }  
 public synchronized void withdraw(int num) {  
      balance = balance - num;  
 }  

 每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待cpu的调度。当一开始线程a第一次执行account.add方法时,jvm会检查锁对象account 的就绪队列是否已经有线程在等待,如果有则表明account的锁已经被占用了,由于是第一次运行,account的就绪队列为空,所以线程a获得了锁,执行account.add方法。如果恰好在这个时候,线程b要执行account.withdraw方法,因为线程a已经获得了锁还没有释放,所以线程 b要进入account的就绪队列,等到得到锁后才可以执行。 
 

3、

Java代码
public class SyncBlock   
  1. {   
  2.     public void method1()   
  3.     {   
  4.         synchronized(this)  // 相当于对method1方法使用synchronized关键字   
  5.         {   
  6.             … …   
  7.         }   
  8.     }   
  9.     public void method2()   
  10.     {   
  11.         synchronized(this)  // 相当于对method2方法使用synchronized关键字   
  12.         {   
  13.             … …   
  14.         }   
  15.     }   
  16.     public synchronized void method3()     
  17.     {   
  18.         … …   
  19.     }   
  20. }  
    public class SyncBlock
    {
        public void method1()
        {
            synchronized(this)  // 相当于对method1方法使用synchronized关键字
            {
                … …
            }
        }
        public void method2()
        {
            synchronized(this)  // 相当于对method2方法使用synchronized关键字
            {
                … …
            }
        }
        public synchronized void method3()  
        {
            … …
        }
    }

  在使用同一个SyncBlock类实例时,这三个方法只要有一个正在执行,其他两个方法就会因未获得同步锁而被阻塞。在使用synchronized块时要想达到和synchronized关键字同样的效果,必须将所有的代码都写在synchronized块中 ,否则,将无法使当前方法中的所有代码和其他的方法同步。

    除了使用this做为synchronized块的参数外,还可以使用ClassName.class(本例为 SyncBlock.this )作为synchronized块的参数来达到同样的效果。

    在内类(InnerClass)的方法中使用synchronized块来时,this只表示内类,和外类(OuterClass)没有关系。但内部类的非静态方法可以和外类的非静态方法同步 。如在内类InnerClass中加一个method4方法,并使method4方法和SyncBlock的三个方法同步,代码如下:

Java代码
  1. public class SyncBlock   
  2. {   
  3.     ...   
  4.     class InnerClass   
  5.     {   
  6.         public void method4()   
  7.         {   
  8.             synchronized(SyncBlock.this)   
  9.             {  ...   }   
  10.         }   
  11.     }   
  12. }  
public class SyncBlock
{
    ...
    class InnerClass
    {
        public void method4()
        {
            synchronized(SyncBlock.this)
            {  ...   }
        }
    }
}

 该例中,InnerClass类的method4方法和SyncBlock类的其他三个方法同步,因此,method1、method2、method3和method4四个方法在同一时间只能有一个方法执行。

静态内部类可以直接创建对象new B.C();
如果内部类不是静态的,那就得这样
B b = new B();
B.C c = b.new C();

 

    Synchronized块不管是正常执行完,还是因为程序出错而异常退出synchronized块,当前的synchronized块所持有的同步锁都会自动释放 。因此,在使用synchronized块时不必担心同步锁的释放问题。

 

4、sychronized关键字只和一个对象实例绑定

Java代码
  1. class Test   
  2. {   
  3.      public synchronized void method()   
  4.      {   }   
  5. }   
  6.     
  7. public class Sync implements Runnable   
  8. {   
  9.      private Test test;   
  10.   
  11.      public Sync(Test test)   
  12.      {   
  13.          this.test = test;   
  14.      }   
  15.   
  16.      public void run()   
  17.      {   
  18.           test.method();   
  19.      }   
  20.         
  21.      public static void main(String[] args) throws Exception   
  22.      {   
  23.          Test test1 =  new Test();   
  24.          Test test2 =  new Test();   
  25.          Sync sync1 = new Sync(test1);   
  26.          Sync sync2 = new Sync(test2);   
  27.          new Thread(sync1).start();   
  28.          new Thread(sync2).start();    
  29.      }   
  30.  }  
  class Test
  {
       public synchronized void method()
       {   }
  }
   
  public class Sync implements Runnable
  {
       private Test test;

       public Sync(Test test)
       {
           this.test = test;
       }

       public void run()
       {
            test.method();
       }
       
       public static void main(String[] args) throws Exception
       {
           Test test1 =  new Test();
           Test test2 =  new Test();
           Sync sync1 = new Sync(test1);
           Sync sync2 = new Sync(test2);
           new Thread(sync1).start();
           new Thread(sync2).start(); 
       }
   }

 上面的代码建立了两个Test类的实例,因此,test1和test2的method方法是分别执行的。要想让method同步,必须在建立Sync类的实例时向它的构造方法中传入同一个Test类的实例。

 

Synchronized与ThreadLocal的区别:

ThreadLocal:http://yunqiang-zhang-hotmail-com.iteye.com/admin/blogs/1350437

一、
synchronized关键字主要解决多线程共享数据同步问题
ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题

ThreadLocal和Synchonized都用于解决多线程并发访问 。但是ThreadLocal与synchronized有本质的区别:

 synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享 。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics