源码均以JDK1.8作为参考

 

1.synchronized初识:

synchronized是Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个对象时,同一时间的另一个线程需要在等待当前线程释放锁的前提下可以继续执行。

对于synchronized关键字的理解是不是很有限呢,记得本人在很早之前被一个面试官问的哑口无言的时候,就下定决心要好好的研究一下synchronized。

在实际工作中synchronized的作用有多大呢?首先synchronized是一个范围的加锁机制,如果使用不当,会大幅度的影响程序的性能。

比如,synchronized在单例模式中的应用。

 

public class Singleton{  
    private static Singleton instance = null;  
    private Singleton(){}  
    public static synchronized Singleton getInstance(){  
        if(null == instance)  
             instance = new Singleton();  
             return instance;  
    }  
}

 

 

此单例模式中利用synchronized同步原语防止多个线程生成多个实例,但是这个synchronized的使用对于效率的影响会很大,当instance实例存在的情况,每次都需要在同

 

 

步的情况下才能获取到实例,所以可以去掉方法的synchronized同步,转而进行代码块的同步:

 

public static Singleton getInstance(){  
    if(null == instance){  
         synchronized(this){  
             if(null == instance)  
                 instance = new Singleton();  
         }  
    }  
    return instance;  
}

 

2.Object锁机制:

在具体介绍synchronized之前,需要对Java中的Object进行简单说明一下,Java中的对象都会有锁的概念,因此在Object类中提供了如wait、notify、notifyAll的方法,就是

为了实现对于对象锁的操作。关于Object先大致介绍一下,详细了解请参照本博客关于Object的文章。

3.synchronized应用场景:

synchronized可以对那些进行修饰:非static方法、非static代码块、static代码块和static方法。这些应用方式,在使用过程中会存在差别。

1) 非static方法

synchronized修饰非static方法的示例如下:

 

public class Test{  
    public synchronized void TestSyn(){...}  
    public synchronized void TestSyn_One(){...}  
}

 

当synchronized修饰的非static方法时,如上的话,被锁定的并不是TestSyn()这个方法,如下:

 

1 Test test = new Test();
2 test.TestSyn();
3 test.TestSyn_One();

 

第1行代码构造了Test类的一个实例test

第2行代码调用了test实例的TestSyn()方法,此时由于synchronized同步原语的存在,会进行同步操作,但是synchronized锁定的并非是TestSyn()这个方法,而且获得test

实例的对象锁进行锁定。

第3行代码调用了test实例的TestSyn_One()方法,若在多线程环境下,第2行和第3行代码存在于两个线程中时,若第2行代码方法区未执行完,第3行代码是无法获得对象

锁,也就是无法执行的。

但是需要注意一点,非static的synchronized操作是针对实例的,只有同一个实例的同一个synchronized或者不同的synchronized方法之间存在着同步关系。不同实例的

synchronized方法之间是不存在同步关系的。

        2) 非static代码块:

synchronized修饰非static代码块的示例如下:

 

synchronized块A:
    synchronized(obj){
        ....
    }
    synchronized块B:
    synchronized(obj){
        ....
    }

 

与修饰非static方法很相似,synchronized修饰的非static代码块同样有着上述的一些特性。

与synchronized修饰非static方法唯一的区别是:synchronized修饰的非static代码块可以指定加锁对象,在作用于方法时,被加锁对象只能是当前对象,作用于代码块时,

该对象obj可以是this,也可以是任何其他自定义的Object实例,如下:

 

public class Test{
    private final Object obj = new Object();
    synchronized(obj){
        ...		
    }
}

 

如代码所示:多个线程到达synchronized代码块时,竞争的是obj实例的锁,而非Test实例的锁。

        3) static方法:

synchronized修饰static方法的示例如下:

 

public class Test{
    public static synchronized void TestSyn(){...}
    public static synchronized void TestSyn_One{...}
}

 

synchronized修饰的static方法或者代码块与synchronized修饰的非static方法或者代码块最大的区别是:锁定内容的不同。

这个与static类型内容的所属有直接关系,static修饰的方法时类方法,代码块也是属于类的,所以在static方法或者代码块使用上与类实例是无关的,与类本身是有关系

的。因此,如示例所示:当调用第2行代码时,锁定的是Test.class这个类,当一个线程调用TestSyn()时,若其他线程想进入TestSyn()或者TestSyn_One()都是不允许的,因为

他们所需要的锁都在第一个线程中未被释放。

  与非static的其他特性相似,当调用一个类的static方法时,那么此类内的所有static方法都是互斥的,它们之间存在着竞争的关系。

        4.static代码块:

 

static synchronized块A:
    static synchronized(XXX.class){
         ....
    }
    static synchronized块B:
    static synchronized(XXX.class){
        ....
    }

 

static修饰的代码块特性与static修饰的方法是一样的,但是对于锁定的对象是不用的,synchronized修饰的static代码块可以指定具体类进行加锁操作,如:

 

public calss Test{
    synchronized(obj){
        ....		
    }
}

 

当调用test实例的TestSyn()方法时,是对test这个实例加锁,此时若是调用test的其他synchronized方法,例如:test.TestSyn_One()也是被禁止的,因为在方法上使用

synchronized会将当前对象锁定,使其所有的synchronized方法之间互斥,拿上面的例子来说,当需要执行synchronized方法时,需要获得test实例的对象锁(此时的锁是互斥

锁),才可以进行操作,当一个synchronized方法正在执行,其他的synchronized方法需要等待对象释放锁才可以进入synchronized方法区执行方法。

 

总结:

synchronized是Java语言级别内置的同步原语,对于非static的方法或者代码块,是在对类的实例进行控制,也就是进行实例对象锁的争夺,但是对于static修饰的方法或者

代码块,由于static修饰的内容不是属于实例而是属于类的,所以此时是对当前类资源的锁进行争夺。

使用synchronized可以保证在多线程环境下代码执行的安全性,但是对于synchronized的原理或者说到底在同步什么、锁定什么,还是要有一定的理解的,这样才可以更好

的进行应用。