`
uule
  • 浏览: 6307676 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

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

 public class ThreadTest{  
   public void test(){  
      Object lock=new Object();  
      synchronized (lock){  
         //do something  
      }  
   }  
 } 

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

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

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的就绪队列,等到得到锁后才可以执行。
参考:http://www.iteye.com/topic/806990
      http://blog.csdn.net/suchfool/archive/2011/01/11/6129833.aspx

 

3、

    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的三个方法 同步,代码如下:

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关键字只和一个对象实例绑定

  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://uule.iteye.com/admin/blogs/870464

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

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

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

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

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

二、API说明

ThreadLocal()
 创建一个线程本地变量。

T get()
 返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。

protected T initialValue()
 返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。
 若该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。

void remove()
 移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。

void set(T value)
 将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。
 在程序中一般都重写initialValue方法,以给定一个特定的初始值。

总结:
 
 ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存 ,但大大减少了 线程同步所带来性能消耗 ,也减少了线程并发控制的复杂度。

ThreadLocal使用的一般步骤:
 1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
 2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
 3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics