Java Synchronized锁的使用详解

目录
  • Synchronized的用法
    • 同步示例方法
    • 同步静态方法
    • 同步代码块

Synchronized的用法

在多线程并发问题中,常用Synchronized锁解决问题。Synchronized锁通常用于同步示例方法,同步静态方法,同步代码块等。

同步示例方法

我们可能自己使用过在方法前加Synchronized锁修饰,在多线程并发同时调用同一个实例化对象时,如果这个方法加上了Synchronized锁,那么也是线程安全的。
举个栗子:

package Thread;

import java.util.stream.IntStream;

public class ThreadTest {
    private Long count=0L;
    public void incrementCount(){
        count++;
    }
    public Long execute() throws InterruptedException {
        Thread thread1=new Thread(()->{
            IntStream.range(0,1000).forEach((i)->incrementCount());//线程1循环1000次
        });
        Thread thread2=new Thread(()->{
            IntStream.range(0,1000).forEach((i)->incrementCount());//线程2循环1000次
        });
        thread1.start();//开启线程
        thread2.start();
        thread1.join();//等待线程1和线程2执行完毕
        thread2.join();
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadTest threadTest=new ThreadTest();
        Long count = threadTest.execute();
        System.out.println(count);
    }
}

在上面的程序中,count变量为成员变量,在多线程同时使用时极大可能会发生错误,在前面也讲到过count++包含三个步骤:1.将变量count从主内存中加载到CPU的寄存器中;2.在CPU的寄存器中执行count++或++count的操作;3.将运算的count++的结果写入缓存或内存中。两个线程都会更新count的值到内存中,当其中一个线程再从内存中读取数据时,可能读到的成员变量会与当前的变量不一致,从而使得最终count的结果不为2000,因此会发生错误。

如何能解决这种错误?就是为incrementCount方法加锁:

 public synchronized void incrementCount(){
        count++;
    }

这样就能保证所得到的count最终值为2000了。

同步静态方法

当一个类的某个静态方法加了synchronized锁时,就相当于给这个类的class对象加锁。所以无论创建多少个当前类的对象调用这个被synchronized锁修饰的静态方法时,都是线程安全的。

如上面的例子,修改如下:

package Thread;

import java.util.stream.IntStream;

public class ThreadTest {
    private static Long count=0L;
    public static synchronized void incrementCount(){
        count++;
    }
    public static Long execute() throws InterruptedException {
        Thread thread1=new Thread(()->{
            IntStream.range(0,1000).forEach((i)->incrementCount());
        });
        Thread thread2=new Thread(()->{
            IntStream.range(0,1000).forEach((i)->incrementCount());
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadTest threadTest=new ThreadTest();
        Long count = threadTest.execute();
        System.out.println(count);
    }
}

因此,当多个线程并发执行调用被synchronized锁修饰的静态方法时,这个静态方法是线程安全的。

同步代码块

前面提到加了synchronized锁的方法在多线程并发条件下是线程安全的,但是在执行业务逻辑过多的代码块时,可能会影响程序的执行效率。对于此时,可以把一个方法分成多个小的临界区。

举个栗子:

    private Long count1=0L;
    private Long count2=0L;
    public synchronized void incrementCount(){
        count1++;
        count2++;
    }

在上面的代码中,count1和count2为两个不同的自增操作,因此对于count1和count2来说是两个不同的临界区资源。当一个线程进入incrementCount方法中时,会对整个方法进行加锁,在对count1进行自增操作时,也会占用count2的资源,相当于占用全部的资源。只有等到这个线程执行完count1++和count2++的操作时,释放锁时,其它线程才能拿到锁资源进入incrementCount方法。

但是这样会影响程序的性能。因为count1++和count2++为两个互不影响的两个临界区资源,当线程拿到锁,会占用两个资源,使得临界区资源进行闲置等待,因此可以优化代码,让synchronized锁修饰代码块。

修改后的代码:

    private Long count1=0L;
    private Long count2=0L;
    public Object count1Lock=new Object();
    public Object count2Lock=new Object();
    public void incrementCount(){
        synchronized (count1Lock){
            count1++;
        }
        synchronized (count2Lock){
            count2++;
        }
    }

上面代码中,对count1和count2分别建立了对象锁count1Lock和count2Lock,而没有对incrementCount加锁,意为当一个线程进入incrementCount方法时,其他线程也能进入此方法,当线程1拿到count1Lock对象锁时,不影响线程2拿到count2Lock对象锁来对count2执行自增操作。

这样既提高了程序的执行效率,同时,由于临界区资源都加了锁,incrementCount方法也是线程安全的。

到此这篇关于Java Synchronized锁的使用详解的文章就介绍到这了,更多相关Java Synchronized锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java必会的Synchronized底层原理剖析

    目录 1. synchronized作用 2. synchronized用法 3. synchronized加锁原理 synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用. 但不可否认的是synchronized依然是并发首选工具,连volatile.CAS.ReentrantLock都无法动摇synchronized的地位.synchronized是工作面试中的必备技能,今天就

  • Java关键字synchronized原理与锁的状态详解

    目录 一.Java中锁的概念 二.同步关键字synchronized特性 1.锁消除示例 2.锁粗化示例 三.synchronized关键字原理 1.关于Mark Word 2.锁的状态变化 (1) 无锁 → 轻量级锁 (2) 轻量级锁 → 重量级锁 (3) 关于偏向锁 (4) 完整的锁升级过程 一.Java中锁的概念 自旋锁:是指当一个线程获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能被成功获取,直到获取到锁才会退出循环. 乐观锁:假定没有冲突,在修改数据

  • Java中synchronized的几种使用方法

    目录 用法简介 1.修饰普通方法 2.修饰静态方法 修饰普通方法 VS 修饰静态方法 3.修饰代码块 this VS class 总结 前言: 在 Java 语言中,保证线程安全性的主要手段是加锁,而 Java 中的锁主要有两种:synchronized 和 Lock,我们今天重点来看一下 synchronized 的几种用法. 用法简介 使用 synchronized 无需手动执行加锁和释放锁的操作,我们只需要声明 synchronized 关键字就可以了,JVM 层面会帮我们自动的进行加锁和

  • Java中synchronized锁升级的过程

    目录 简介 CAS markWord Synchronized的锁升级 偏向锁 轻量级锁 重量级锁 总结 简介 在多线程中解决线程安全的问题时常用到Synchronized,现在的synchronized相对于早期的synchronized做出了优化,从以前的加锁就是重量级锁优化成了有一个锁升级的过程(偏向锁->轻量级锁->重量级锁). CAS cas的全称是compare and swap,从名称上可以看出它是先比较再进行设置,它是一种在多线程环境下实现同步功能的机制. 下面这段代码是在Re

  • 详解Java Synchronized的实现原理

    目录 Synchronized Synchronized的使用方式 Synchronized的底层实现 1.Java对象头 2.Monitor 3.线程状态流转在Monitor上体现 Synchronized 的锁升级 谈到多线程就不得不谈到Synchronized,重要性不言而喻,今天主要谈谈Synchronized的实现原理. Synchronized synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是同步,也称之为”同步锁“.

  • Java Synchronized锁的使用详解

    目录 Synchronized的用法 同步示例方法 同步静态方法 同步代码块 Synchronized的用法 在多线程并发问题中,常用Synchronized锁解决问题.Synchronized锁通常用于同步示例方法,同步静态方法,同步代码块等. 同步示例方法 我们可能自己使用过在方法前加Synchronized锁修饰,在多线程并发同时调用同一个实例化对象时,如果这个方法加上了Synchronized锁,那么也是线程安全的.举个栗子: package Thread; import java.ut

  • java synchronized 锁机制原理详解

    目录 前言: 1.synchronized 的作用: 2.synchronized 底层语义原理: 3. synchronized 的显式同步与隐式同步: 3.1.synchronized 代码块底层原理: 3.2.synchronized 方法底层原理: 4.JVM 对 synchronized 锁的优化: 4.1.锁升级:偏向锁->轻量级锁->自旋锁->重量级锁 4.1.1.synchronized 的 Mark word 标志位: 4.1.2.锁升级过程: 4.2.锁消除: 4.3

  • java自旋锁和JVM对锁的优化详解

    目录 背景 好处 AtomicLong的实现 getAndIncrement方法 实验 缺点 适用场景 JVM对锁做了哪些优化? 自适应的自旋锁 锁消除 锁粗化 偏向锁/ 轻量级锁/ 重量级锁 锁升级 背景 先上图 由此可见,非自旋锁如果拿不到锁会把线程阻塞,直到被唤醒: 自旋锁拿不到锁会一直尝试 为什么要这样? 好处 阻塞和唤醒线程都是需要高昂的开销的,如果同步代码块中的内容不复杂,那么可能转换线程带来的开销比实际业务代码执行的开销还要大. 在很多场景下,可能我们的同步代码块的内容并不多,所以

  • java非公平锁知识点实例详解

    1.非公平锁不能保证锁的获取是按照请求锁的顺序进行的.这可能会导致某个或某些线程永远得不到锁. 2.CPU唤醒线程的费用可以降低,整体吞吐效率会很高.但是可能会有线程长时间甚至永远得不到锁,导致饿死. 实例 /** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 731615356378282

  • JAVA线程sleep()和wait()详解及实例

    JAVA线程sleep()和wait()详解及实例 sleep 1.sleep是Thread的一个静态(static)方法.使得Runnable实现的线程也可以使用sleep方法.而且避免了线程之前相互调用sleep()方法,引发死锁. 2.sleep()执行时需要赋予一个沉睡时间.在沉睡期间(阻塞线程期间),CPU会放弃这个线程,执行其他任务.当沉睡时间到了之后,该线程会自动苏醒,不过此时线程不会立刻被执行,而是要等CPU分配资源,和其他线程进行竞争. 3.此外如果这个线程之前获取了一个机锁,

  • 关于多线程常用方法以及对锁的控制(详解)

    1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是如果有Synchronized同步块,其他线程仍然不同访问共享数据.注意该方法要捕获异常 比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完成后,低优先级的线程才能执行:但当高优先级的线程sleep(5000)后,低优先级就有机会执行了. 总之

  • Java多线程用法的实例详解

    Java多线程用法的实例详解 前言: 最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); p

  • java并发编程之cas详解

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值.这听起来可能有一点复杂但是实际上你理解之后发现很简单,接下来,让我们跟深入的了解一下这项技术. CAS的使用场景 在程序和算法中一个经常出现的模式就是"check and act"模式.先检查后操作模式发生在代码中首先检查一个变量的值,然后再基于这个值做一些操作.下面是一个

  • java集合中的list详解

    1.List接口 该接口定义的元素是有序的且可重复的.相当于数学里面的数列,有序可重复 booleanaddAll(intindex,Collection<?extendsE>c);将指定集合中所有元素,插入至本集合第index个元素之后defaultvoidreplaceAll(UnaryOperatoroperator);替换集合中每一个元素值defaultvoidsort(Comparator<?superE>c);给集合中的元素进行排序Eget(intindex);获取集合

随机推荐