如何使用synchronized实现同步访问,在java5.0之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock。此处简单介绍了如何使用synchronized防止并发访问,和使用synchronized存在的缺陷,以及使用Lock来控制程序的并发访问。

并发编程三个概念 原子性: 一个操作或多个操作要么全部执行且执行过程不被中断,要么不执行 可见性: 多个线程修改同一个共享变量时,一个线程修改后,其他线程能马上获得修改后的值 有序性 : 程序执行的顺序按照代码的先后顺序执行

一、synchronized关键字是为了线程安全服务的,线程安全就是当多个线程访问一个类(变量或方法)的时候,这个对象都可以表现出正常的行为。那线程的安全性又表现出三个特性:原子性、可见性及有序性。synchronized保证了同步,原子性。volatile关键字保证了可见性。wait,notify 负责多个线程之间的通信。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

synchronized 可以在任意对象及方法上加锁,若一个线程想要执行synchronized修饰的代码块,首先要

step1 尝试获得锁
step2 如果拿到锁,执行synchronized代码体内容
step3 如果拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。

注*(线程多了也就是会出现锁竞争的问题,多个线程执行的顺序是按照CPU分配的先后顺序而定的,而并非代码执行的先后顺序)

synchronized 可以修饰方法,修饰代码块,这些都是对象锁。若和static一起使用,则升级为类锁。建议修饰代码块,减小锁的颗粒度,提高系统的性能。
synchronized 锁是可以重入的,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。锁重入的机制,也支持在父子类继承的场景。
synchronized 同步异步,一个线程得到了一个对象的锁后,其他线程是可以执行非加锁的方法(异步)。但是不能执行其他加锁的方法(同步)。
synchronized 锁异常,当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

    private void thisLock () { // 使用对象锁
      synchronized (this) {
        System.out.println("this 对象锁!");
      }
    }

 

    private void classLock () { // 使用类锁
      synchronized (ITDragonSynchronized.class) {
        System.out.println("class 类锁!");
      }
    }

目前可以使用的场景,在使用单例模式的时候可以使用synchronized关键字,进行二次验证,防止在并发的情况下创建出多个对象。

      // 公共静态成员方法,返回唯一实例
      public static LoadBalancer GetLoadBalancer(){
        // 第一重判断
        if (instance == null){
          // 锁定代码块
          synchronized(syncLocker){
            // 第二重判断
            if (instance == null){
              instance = new LoadBalancer();
            }
          }
        }
        return instance;
      }

二、volatile关键字虽然不具备synchronized关键字的原子性(同步)但其主要作用就是使变量在多个线程中可见。也就是可见性。用法很简单,直接用来修饰变量。因为其不具备原子性。

volatile 关键字工作原理每个线程都有自己的工作内存,如果线程需要用到一个变量的时,会从主内存拷贝一份到自己的工作内存中。从而提高了效率。每次执行完线程后再将变量从工作内存同步回主内存中。

这样就存在一个问题,变量在不同线程中可能存在不同的值。如果用volatile 关键字修饰变量,则会让线程的执行引擎直接从主内存中获取值。

 Java 并发编程synchronized关键字和Lock关键字 随笔

三、synchronized的缺陷

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

2)线程执行发生异常,此时JVM会让线程自动释放锁;

因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到,注意:

1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;

2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象

使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生

先要说明的就是Lock,通过查看Lock的源码可知,Lock是一个接口:

public interface Lock {

     void  lock();      void  lockInterruptibly()  throws  InterruptedException;      boolean  tryLock();      boolean  tryLock( long  time, TimeUnit unit)  throws  InterruptedException;      void  unlock();      Condition newCondition(); }   对Lock想细致了解的可以查看相关文档 https://www.cnblogs.com/shoshana-kong/p/9076791.html

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄