Java Synchronized的总结

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操作,判断内存中的当前值与预期值是否相等,若相等则修改为新值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值