详解Java中的悲观锁与乐观锁

yizhihongxing

详解Java中的悲观锁与乐观锁

什么是锁?

在多线程编程中,为了保证线程安全和数据一致性,我们常常采用锁机制。锁顾名思义就是在一段代码区域加上一个锁,使得同一时刻只有一个线程可以访问该代码区域。Java中的锁机制主要有两种:悲观锁和乐观锁。

悲观锁

悲观锁的思想就是认为并发情况下不同线程之间会发生冲突,因此在整个处理过程中,都加上了同步锁,让线程独占资源,其他线程等待。

Java中常见的悲观锁实现方式是synchronized关键字,例如:

public synchronized void method(){
    // 该方法的代码块
}

synchronized关键字会自动加锁,当线程执行synchronized代码块时,其他线程无法访问该代码块,只能等待。

乐观锁

乐观锁的思想则是相反的,它认为并发情况下不同线程之间不会发生冲突,因此不需要加锁。在更新数据时,判断该数据有没有被其他线程修改,若该数据被修改,则暂停该次操作,重新读取数据并重试。

Java中的乐观锁实现方式一般是通过CAS(Compare And Swap)来实现,例如:

public class Counter {
    private AtomicInteger num = new AtomicInteger(0);

    public void increment() {
        int newVal;
        do {
            newVal = num.get() + 1;
        } while(!num.compareAndSet(num.get(), newVal));
    }
}

CAS先获取内存中的值,然后基于获取的值来计算新值,最后通过CAS操作来尝试更新内存中的值。如果这个值不是期望的,那么修改失败,就需要重试。

悲观锁与乐观锁的选择

在选择使用悲观锁还是乐观锁时,需要根据实际情况来进行考虑。

悲观锁适合于写操作比较多的情况,因为写操作需要独占资源。悲观锁在实现简单的同时也存在着性能问题,因为当访问量大时,悲观锁会降低系统的吞吐量。

乐观锁适合于读操作比较多的情况,因为读操作不需要独占资源,多个线程可以共同读取一个数据。并且乐观锁的实现方式可以避免了加锁引起的性能问题。

示例

下面两个示例演示了悲观锁和乐观锁的实现方式。其中线程安全的计数器Counter类用于演示两种锁的实现方式。

悲观锁示例

public class PessimisticLockExample {
    private static int value = 0;

    public static synchronized void increment() {
        value++;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    increment();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    increment();
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("PessimisticLockExample value: " + value);
    }
}

乐观锁示例

public class OptimisticLockExample {
    private static AtomicInteger value = new AtomicInteger(0);

    public static void increment() {
        int oldValue;
        int newValue;
        do {
            oldValue = value.get();
            newValue = oldValue + 1;
        } while(!value.compareAndSet(oldValue, newValue));
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    increment();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    increment();
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("OptimisticLockExample value: " + value);
    }
}

总结

悲观锁的实现方式简单直接,但存在性能问题,适合于写操作比较多的情况。乐观锁的实现方式可以避免加锁带来的性能问题,适合于读操作比较多的情况。在实际情况中,需要根据具体情况选择合适的锁实现方式来保证线程安全和数据一致性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java中的悲观锁与乐观锁 - Python技术站

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

相关文章

  • java实现的AES加密算法完整实例

    下面是“Java实现的AES加密算法完整实例”的完整攻略: 一、概述 AES(Advanced Encryption Standard)是一种常用的对称加密算法,之前常用的DES算法已经不再安全。在Java中,可以通过javax.crypto包中的AES算法实现加密和解密。 二、实现步骤 生成AES密钥 KeyGenerator kgen = KeyGene…

    Java 2023年5月19日
    00
  • XML经典问答

    XML经典问答攻略 本文将为您提供针对XML经典问题的攻略,以解决常见的XML相关问题。以下是您需要注意的几个方面: 1. XML文档结构 XML文件通常由一个根元素(root element)组成,并由开始标签和结束标签加以表示。中间可以嵌套若干子元素。元素可以包含属性(attribute)或文本(text)。如下所示: <?xml version=…

    Java 2023年5月20日
    00
  • Spring Security 实现用户名密码登录流程源码详解

    让我来详细讲解一下“Spring Security 实现用户名密码登录流程源码详解”的完整攻略。 一、说明 Spring Security 是一个基于 Spring 的安全框架,可以提供完整的安全性解决方案,包括认证、授权、攻击防护等方面的功能。 在本攻略中,我们将深入了解 Spring Security 如何实现基于用户名密码的登录流程,并分析其源码实现细…

    Java 2023年6月3日
    00
  • 4种java复制文件的方式

    当需要对文件进行复制操作时,可以采用Java的文件IO流来实现。下面介绍4种Java复制文件的方式。 1.使用FileChannel实现文件复制 通过FileChannel实现文件复制的方式,可以使用FileInputStream、FileOutputStream或RandomAccessFile打开文件通道,使用transferFrom或transferT…

    Java 2023年5月20日
    00
  • Java C++算法题解leetcode801使序列递增的最小交换次数

    让我来详细讲解一下“Java C++算法题解leetcode801使序列递增的最小交换次数”的完整攻略。 问题描述 题目名称:使序列递增的最小交换次数 题目描述:给定一个数组 nums,你需要将数组连续的子序列进行升序排列,使得最终得到的数组是递增的。请你计算并返回最少的交换次数,使得数组满足题意。 示例 1: 输入:nums = [1,3,5,4,2,6,…

    Java 2023年5月26日
    00
  • 垃圾收集器接口的作用是什么?

    以下是关于垃圾收集器接口的详细讲解: 什么是垃圾收集器接口? 垃圾收集器接口是 Java 虚拟机提供的一组接口,用于实现自定义的垃圾收集器。通过实现垃圾收集器接口,可以自定义垃圾收集器的行为和策略,以满足不同的应用场景和需求。 垃圾收集器接口包括以下几个接口: Collector:垃圾收集器接口,定义了垃圾收集的基本行为和策略。 MemoryPoolMXBe…

    Java 2023年5月12日
    00
  • 一分钟入门Java Spring Boot彻底解决SSM配置问题

    下面我来详细讲解一下“一分钟入门Java Spring Boot彻底解决SSM配置问题”的完整攻略。 简介 Java Spring Boot是一个基于Spring Framework的快速开发框架,它可以简化Spring应用开发过程,在保持Spring优点的同时去除了其缺点。Spring Boot提供了一种快速配置、轻量级的应用开发方式,开发者只需要少量的配…

    Java 2023年5月19日
    00
  • Java 判断两个字符串是否由相同的字符组成的实例

    下面是“Java 判断两个字符串是否由相同的字符组成的实例”的完整攻略。 鉴于这个问题,我们需要一个逐字比较的算法来解决。首先,需要确保两个字符串的长度相等,然后对它们进行排序,最后逐一比较它们是否相等。下面是具体步骤: 确保两个字符串的长度相等。可以使用 length() 方法来获取两个字符串的长度,并使用 if 语句确定它们是否相等,如果不相等,马上返回…

    Java 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部