Synchronized根据获取锁的分类
- 获取对象锁
- 同步代码块Synchronized(this)或者Synchronized(Object)
- 非静态同步方法Synchronize method
- 获取类锁
- 同步代码块Synchronized(AA.class)
- 静态同步方法static Synchronized method
对象锁与类锁的区别?
- 有线程访问对象的同步代码块时,其他线程可以访问该对象的非同步代码块
- 若锁住的是同一个对象,一个线程在访问对象的同步代码块或者同步方法时,其他线程进入阻塞状态
- 同一个类的不同对象的对象锁互不干扰
- 类锁是一种特殊的锁,当其所有对象公用一个类锁时,则代码同步
- 类锁与对象锁互不干扰
关于对象锁与类锁的代码测试
package com.bdcloud.thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SynchronizedDemo implements Runnable {
@Override
public void run() {
String tName = Thread.currentThread().getName();
if (tName.startsWith("A")){
asyncMethod();
}else if(tName.startsWith("B")){
syncorizeObjectMethod();
}else if (tName.startsWith("C")){
synchronizeMethod();
}else if (tName.startsWith("D")){
synchronizeClassMethod();
}else{
synchronizeStaticMethod();
}
}
/**
* 异步方法
*/
private void asyncMethod() {
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --Strart---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current Thread is "+Thread.currentThread().getName()+" ---END------"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
/***
* Synchronize同步对象
* @throws InterruptedException
*/
private void syncorizeObjectMethod() {
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --Strart---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
synchronized (this){
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --getLock---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current Thread is "+Thread.currentThread().getName()+" ---END------"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
/***
* 同步方法
*/
private synchronized void synchronizeMethod() {
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --Strart---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --getLock---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current Thread is "+Thread.currentThread().getName()+" ---END------"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
/***
* 类锁
*/
private void synchronizeClassMethod(){
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --Strart---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
synchronized (SynchronizedDemo.class){
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --getLock---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current Thread is "+Thread.currentThread().getName()+" ---END------"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
/***
* 同步静态方法
*/
private synchronized static void synchronizeStaticMethod() {
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --Strart---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println("Current Thread is "+Thread.currentThread().getName()+" --getLock---"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current Thread is "+Thread.currentThread().getName()+" ---END------"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
public static void main(String[] args) {
SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
Thread A1 = new Thread(synchronizedDemo,"A1");
Thread A2 = new Thread(synchronizedDemo,"A2");
Thread B1 = new Thread(new SynchronizedDemo(),"B1");
Thread B2 = new Thread(new SynchronizedDemo(),"B2");
Thread C1 = new Thread(synchronizedDemo,"C1");
Thread C2 = new Thread(synchronizedDemo,"C2");
Thread D1 = new Thread(synchronizedDemo,"D1");
Thread D2 = new Thread(synchronizedDemo,"D2");
Thread E1 = new Thread(synchronizedDemo,"E1");
Thread E2 = new Thread(synchronizedDemo,"E2");
// A1.start();
// A2.start();
B1.start();
B2.start();
D1.start();
D2.start();
}
}
Synchronized的底层实现原理
对象在堆内存中的布局有三部分组成
- 对象头
- Mark Word:默认存储对象的hashcode,分代年龄,锁类型、锁标志位等信息
- Class Metadata Address:类型指针指向对象所属的类的元数据空间地址
- 实例数据
- 对齐填充
JVM在创建一个对象的时候都会创建一个Monitor对象,其存放在对象头中,Synchronized锁住的就是该对象
volatile变量
Volatile变量用来保证共享变量的可见性,其通过内存屏障来实现共享变量对各个线程的可见性,强制线程对共享变量的修改刷新写入主存,其他线程读取的时候设置本地缓存失效,强制从主存读取最新的值
比较明显的特点
- 禁止指令重排序
- 保证程序的可见性,单并保证原子性
- 常用于boolean型判断线程执行流程,以及双重校验锁实现单例模式
//双重校验锁的实现
package com.bdcloud.test;
public class SingleInstance {
private volatile SingleInstance instance = null;
public SingleInstance getInstance(){
//第一次校验
if (instance == null){
synchronized (SingleInstance.class){
//第二次校验
if (instance == null){
instance = new SingleInstance();
}
}
}
return instance;
}
}
第一次校验的作用是避免多线程环境下多次竞争类锁,若不为空则直接返回对象
第二次校验是在多线程环境下,线程A已经进入初始化对象的过程中,但在此时因为线程调度令其释放类锁线程B获取到类锁,若不加这次非空判断,则直接走创建对象的流程,则会导致堆中创建多个对象
疑问?为什么需要volatile变量修饰instance
因为依据volatile的特性,通过内存屏障实现禁止指令重排序,创建对象的过程可以分为三个步骤
- 第一,申请堆内存空间
- 第二,执行对象初始化
- 第三,将对象的头指针指向申请空间的收地址
根据JVM的优化策略,有可能导致第二步与第三步互换,从而导致对象未初始化完成,但该对象已经不为空了,其他线程执行getInstance方法后直接返回对象,但是该对象还是空的;故而引入volatile变量禁止指令重排序,保证该对象线程安全
Synchronized与ReentrantLock的区别(AbstructQueuedSynchronizer简称AQS)
- Synchronized可以修饰方法,修饰代码块
- ReentrantLock与 CountDownLatch、FutureTask、Semaphore一样基于AQS实现
- 能够实现比Synchronized更细粒度的控制,可以控制fairness(公平性)
- 调用Lock()之后,必须调用unlock()释放锁
- 性能未必比Synchronized高,并且是可重入的
- ReentrantLock可以判断是否有线程。或某个特定线程在排队等待获取锁
- ReentrantLock带超时的获取锁的尝试
- ReentrantLock可以判断是否获取到锁
- Synchronized底层使用对象的Mark Words实现,ReentrantLock底层使用UnSafe.park()
ReentrantLock与Condition对比synchronized与wait/notify
package com.bdcloud.test;
import java.util.concurrent.locks.ReentrantLock;
public class RantentLock implements Runnable{
//设置是否为公平锁
private static ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while (true){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"get lock");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
RantentLock demo = new RantentLock();
new Thread(demo).start();
new Thread(demo).start();
}
}
CAS (compare and sweep)
底层native lib中的cas操作,判断内存中的当前值与预期值是否相等,若相等则修改为新值