一. 定义
对于每一个对象,有且仅有一个同步锁;不同的线程能共同访问该同步锁。但是,在同一个时间点,该同步锁能且只能被一个线程获取到。不同线程对同步锁的访问是互斥的,不同的加锁方式不互斥",协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。他是属于独占式的悲观锁,同时属于可重入锁。
二、原理
进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1; 当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。 含义:(monitor 机制) Synchronized 是在加锁,加对象锁。 对象锁是一种重量锁(monitor),
synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、 对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。针对同步锁的升级,会专门出一篇博客。
三、synchronized方法 和 synchronized代码块
同步方法 (在方法声明上加synchronize):同步方法的锁是固定的this
同步代码块 (锁的对象可以是任意对象): 同步代码块的锁是任意的对象。
建议使用同步代码块。因为同步函数的锁唯一的,只能是this,最好使用同步块来减少锁定范围提高并发效率。
四、实例锁 和 全局锁
实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念)
全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁) "全局锁对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)" 同步代码块中锁对象能是任意的对象;多个线程时,用同一个锁对象才能保证线程安全。
同步方法中的锁对象是 this 静态同步方法中的锁对象是 类名.class
一个很经典的示例(通过不同的锁的类型来理解实例锁和全局锁)
pulbic class Something { public synchronized void isSyncA(){} public synchronized void isSyncB(){} public static synchronized void cSyncA(){} public static synchronized void cSyncB(){} } 假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况。 (01) x.isSyncA()与x.isSyncB() (02) x.isSyncA()与y.isSyncA() (03) x.cSyncA()与y.cSyncB() (04) x.isSyncA()与Something.cSyncA() (01) 不能被同时访问。因为isSyncA()和isSyncB()都是访问同一个对象(对象x)的同步锁! (02) 可以同时被访问。因为访问的不是同一个对象的同步锁,x.isSyncA()访问的是x的同步锁,而y.isSyncA()访问的是y的同步锁。 (03) 不能被同时访问。因为cSyncA()和cSyncB()都是static类型,x.cSyncA()相当于Something.isSyncA(),y.cSyncB()相当于Something.isSyncB(),因此它们共用一个同步锁,不能被同时反问。 (04) 可以被同时访问。因为isSyncA()是实例方法,x.isSyncA()使用的是对象x的锁;而cSyncA()是静态方法,Something.cSyncA()可以理解对使用的是“类的锁”。因此,它们是可以被同时访问的。
五、同步锁的适用场景
一般用于对同一资源竞争比较小的情况下。
六、什么时候释放锁
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁。