Java 多线程同步 锁机制与synchronized深入解析

Java 多线程同步 锁机制与synchronized深入解析

在Java多线程编程中,为了保证线程安全,我们需要使用同步机制来避免多个线程同时访问共享资源造成数据不一致等问题。其中最常用的同步机制就是锁机制。

锁机制

锁机制就是控制多个线程访问共享资源的方式,一般来说,对于共享资源的访问,我们需要通过获取锁来限制只有一个线程可以访问,其他线程需要等待当前线程释放锁后才能访问。Java中锁主要分为两种,分别是对象锁和类锁。

对象锁

对象锁就是使用synchronized关键字修饰的方法和代码块,进入synchronized代码块前需要先获取锁,其他线程需要等待锁的释放后才能进入代码块。例如:

public synchronized void method() {
    // 操作共享资源的代码
}

在上面的代码中,synchronized修饰了method()方法,这时method()方法就成为了一个同步方法,其他线程想要执行该方法,先需获取该方法所属对象的锁。如果另一个线程在获取锁之前进入了该方法,则需要等待前一个线程执行完并释放锁后才能执行。

除了synchronized修饰方法外,我们还可以使用synchronized关键字修饰代码块。例如:

public void method() {
    synchronized(this) {
        // 操作共享资源的代码
    }
}

在上面的代码中,synchronized修饰了代码块,this表示当前对象锁,进入代码块前需要先获取锁,其他线程需要等待锁的释放后才能进入。

类锁

类锁就是对类的锁定,在Java中我们可以使用synchronized来实现类锁,示例如下:

public static synchronized void method() {
    // 操作共享资源的代码
}

在上面的代码中,synchronized修饰了静态方法,此时该方法就成为了一个类锁。其他线程想要执行该方法,需要获取该类的锁,如果在获取锁之前有其他线程访问该方法,需要等待获取锁。

synchronized深入解析

synchronized是Java中最常用的同步机制之一,它保证了同一时刻只有一个线程可以执行同步代码块或同步方法。从底层实现来看,synchronized分为偏向锁、轻量级锁、重量级锁三种,下面我们来深入了解一下。

偏向锁

偏向锁是一种优化锁机制的手段,它是从JDK 1.6开始引入的。偏向锁的目标是减少获得锁的代价,也就是当同步代码块执行时,只需要一次CAS操作就可以让线程获得锁,减少了获取锁和释放锁的相关操作。

偏向锁是指一种针对加锁操作的优化模式,Java中使用了轻量级锁和重量级锁,其中轻量级锁占用的资源更少,但是切换到重量级锁的时候需要更多的资源,偏向锁则是针对锁竞争不激烈的情况下提供的优化模式,也是轻量级锁和重量级锁之前的过度状态。

轻量级锁

轻量级锁是解决竞争不激烈情况下的线程同步方式,轻量级锁使用CAS操作来实现,CAS操作是一种乐观锁思路,假定竞争是不激烈的,当有线程竞争时,CAS会重试直到成功为止。

轻量级锁的使用流程如下:

  1. 判断对象头是否为轻量级锁,如果是,执行2,否则执行4。

  2. 使用CAS操作尝试将对象头里的线程ID改为当前线程ID,如果成功,线程进入临界区执行同步操作,如果失败,说明有其他线程竞争,执行3.

  3. 如果CAS操作失败,则说明竞争激烈,此时使用自旋锁进行等待,等待一定的时间后如果还不能获得锁,线程会自动膨胀为重量级锁。

  4. 如果对象头不是轻量级锁,则锁膨胀为重量级锁。

重量级锁

当多个线程竞争同一把锁时,轻量级锁无法提供足够的保障,此时就会升级为重量级锁,重量级锁是基于操作系统内核来实现的,与操作系统内核的Mutex锁类似,重量级锁会切换线程上下文,代价非常高昂,所以尽量避免使用。

示例说明

下面通过两个具体的示例,详细说明synchronized锁机制以及上述所提到的偏向锁、轻量级锁和重量级锁的实际运用和效果。

示例1:并发访问HashMap导致程序异常

在开发过程中,我们可能需要同时操作一个HashMap,这时多个线程可能会同时访问同一个Hash桶,导致数据异常,例如:

public class HashMapTest {
    private HashMap<String, String> hashMap = new HashMap<>();

    public void testHashMap() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                hashMap.put(UUID.randomUUID().toString(), "");
            }, String.valueOf(i)).start();
        }
    }
}

在上面的代码中,我们创建了10个线程并发地向一个HashMap中添加元素,由于HashMap非线程安全,所以在多线程并发操作下,可能会发生数据不一致等问题。

为了解决这个问题,我们可以使用synchronized来保证线程安全,例如:

public class HashMapTest {
    private HashMap<String, String> hashMap = new HashMap<>();

    public synchronized void testHashMap() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                hashMap.put(UUID.randomUUID().toString(), "");
            }, String.valueOf(i)).start();
        }
    }
}

在上面的代码中,我们将testHashMap()方法加上synchronized关键字,这样可以保证在同一时刻只有一个线程能够执行该方法,从而保证并发访问HashMap时不会发生数据异常等问题。

示例2:测试锁机制的实际效果

为了测试锁机制的实际效果,我们可以使用JMH进行单线程和多线程的压测,例如:

public class SynchronizedTest {
    private static Object lockObject = new Object();

    @Benchmark
    @Threads(1)
    public void testSyncSingleThread() {
        synchronized (lockObject) {
            // 临界区代码
        }
    }

    @Benchmark
    @Threads(4)
    public void testSyncMultiThread() {
        synchronized (lockObject) {
            // 临界区代码
        }
    }
}

在上面的代码中,我们使用JMH来测试单线程和多线程下synchronized锁机制的性能。

结果显示,在多线程情况下,加锁能够明显提高程序的性能,可以有效避免多个线程并发访问同一个共享资源的问题。同时,在峰值时,锁的效果也最为明显。

总之,synchronized是Java中最常用的同步机制之一,它通过锁机制来控制多个线程对共享资源的访问,保证同一时刻只有一个线程可以执行同步代码块或同步方法。对于锁机制的性能问题,我们可以使用JMH进行压测来调优。在实际运用中,我们需要针对具体场景来选择合适的锁机制,以达到最优的性能表现。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 多线程同步 锁机制与synchronized深入解析 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • Java并发编程:CountDownLatch与CyclicBarrier和Semaphore的实例详解

    Java并发编程:CountDownLatch与CyclicBarrier和Semaphore的实例详解 介绍 本篇文章主要讲解Java并发编程中的三种常用同步工具:CountDownLatch、CyclicBarrier和Semaphore。这三种工具都可以用于协调线程的执行,但实现的方式有所不同。 CountDownLatch:用于等待多个线程执行完毕后…

    多线程 2023年5月17日
    00
  • Java基础之多线程

    Java多线程的基础知识 在 Java 编程中,多线程是非常常见的技术,多线程的使用可以在提高程序并发性能的同时,也增加了程序的复杂度,因此学好多线程技术对于 Java 开发人员来说是非常重要的。 1. 创建线程 在 Java 中创建一个线程有两种主要方法: 1.1. 实现 Runnable 接口 Runnable 接口是 Java 多线程中的一个基本接口,…

    多线程 2023年5月17日
    00
  • Java并发编程Semaphore计数信号量详解

    Java并发编程Semaphore计数信号量详解 介绍 Semaphore(信号量)是一个经典的并发编程工具,被广泛应用于各种应用场景,如资源池、限流等。Semaphore 给予我们对并发调度这个宏观的掌控权。 在 Java 5 中,Semaphore 正式被纳入了 Java 并发包,并成为了并发编程中一个必不可少的类。Semaphore 是一个计数信号量,…

    多线程 2023年5月16日
    00
  • 基于Java回顾之多线程详解

    基于Java回顾之多线程详解 Java作为一门支持多线程编程的语言,多线程编程已经成为JVM生态中极为重要的编程技巧之一。Java提供了许多多线程编程的API及相关库,可以轻松实现多线程程序。本文将从以下几个方面来详细讲解Java多线程编程的相关知识: 多线程基础概念 多线程编程的五种方式 多线程的同步与锁机制 Java 线程池 多线程基础概念 在Java多…

    多线程 2023年5月17日
    00
  • Java面试必备八股文整理

    首先我们先来了解一下什么是“八股文”。在面试中,某些问题或者某些知识点会被高频度地问到,这时就出现了某些标准的问法和答案,而这些标准的问法和答案就被称为“八股文”。接下来,我们就来详细讲解一下关于Java面试必备八股文整理的完整攻略。 什么是Java面试必备八股文整理 Java面试必备八股文整理,就是针对Java面试中最常被问到的一些问题和知识点进行整理,形…

    多线程 2023年5月17日
    00
  • 从并发到并行解析Go语言中的sync.WaitGroup

    从并发到并行解析Go语言中的sync.WaitGroup是一篇介绍Go语言中并发编程工具的文章。在该篇文章中,我们会深入了解到什么是并发和并行,以及如何使用sync.WaitGroup来协调并发和并行工作。 并发和并行的定义 并发是指同时执行多个代码段,但并不保证这些代码段的执行顺序。一个被操作系统调度器管理的Go程序就是一个并发程序。 并行是指同时执行多个…

    多线程 2023年5月16日
    00
  • Python实现的HTTP并发测试完整示例

    这里是关于 “Python实现的HTTP并发测试完整示例” 的完整攻略。 前言 在对一个Web服务器进行压力测试时,一个重要的方面是能够模拟多个并发请求以测试其性能。在Python中,我们可以使用多种库来实现HTTP并发测试。本文将涵盖使用concurrent.futures和asyncio库实现HTTP并发测试的两个示例。 易于使用的concurrent.…

    多线程 2023年5月16日
    00
  • python实现多进程并发控制Semaphore与互斥锁LOCK

    Python提供了多种在多进程中保证资源同步与控制的工具,其中Semaphore和互斥锁(读-写锁)是最常用的。 Semaphore 控制并发数 Semaphore(信号量)可以用来控制并发进程数,通过设置一个并发的数量(也就是信号量),后续的进程就会通过信号量来控制并发,避免进程数量过多导致系统资源不足。通过Semaphore控制同一时间只有一定数量的进程…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部