当前位置:首页 > 科技  > 软件

Java的ConcurrentHashMap是使用的分段锁?

来源: 责编: 时间:2024-02-01 12:51:54 269观看
导读了不起在前两天的时候给大家讲述了关于这个 Java 的公平锁,非公平锁,共享锁,独占锁,乐观锁,悲观锁,递归锁,读写锁,今天我们就再来了解一下其他的锁,比如,轻量级锁,重量级锁,偏向锁,以及分段锁。轻量级锁Java的轻量级锁(Lightweight

了不起在前两天的时候给大家讲述了关于这个 Java 的公平锁,非公平锁,共享锁,独占锁,乐观锁,悲观锁,递归锁,读写锁,今天我们就再来了解一下其他的锁,比如,轻量级锁,重量级锁,偏向锁,以及分段锁。rzk28资讯网——每日最新资讯28at.com

轻量级锁

Java的轻量级锁(Lightweight Locking)是Java虚拟机(JVM)中的一种优化机制,用于减少多线程竞争时的性能开销。在多线程环境中,当多个线程尝试同时访问共享资源时,通常需要某种形式的同步以防止数据不一致。Java提供了多种同步机制,如synchronized关键字和ReentrantLock,但在高并发场景下,这些机制可能导致性能瓶颈。rzk28资讯网——每日最新资讯28at.com

轻量级锁是JVM中的一种锁策略,它在没有多线程竞争的情况下提供了较低的开销,同时在竞争变得激烈时能够自动升级到更重量级的锁。这种策略的目标是在不需要时避免昂贵的线程阻塞操作。rzk28资讯网——每日最新资讯28at.com

不过这种锁并不是通过Java语言直接暴露给开发者的API,而是JVM在运行时根据需要自动应用的。因此,我们不能直接通过Java代码来实现一个轻量级锁。rzk28资讯网——每日最新资讯28at.com

但是我们可以使用Java提供的synchronized关键字或java.util.concurrent.locks.Lock接口(及其实现类,如ReentrantLock)来创建同步代码块或方法,这些同步机制在底层可能会被JVM优化为使用轻量级锁。rzk28资讯网——每日最新资讯28at.com

示例代码:rzk28资讯网——每日最新资讯28at.com

public class LightweightLockExample {        private Object lock = new Object();      private int sharedData;        public void incrementSharedData() {          synchronized (lock) {              sharedData++;          }      }        public int getSharedData() {          synchronized (lock) {              return sharedData;          }      }        public static void main(String[] args) {          LightweightLockExample example = new LightweightLockExample();            // 使用多个线程来访问共享数据          for (int i = 0; i < 10; i++) {              new Thread(() -> {                  for (int j = 0; j < 1000; j++) {                      example.incrementSharedData();                  }              }).start();          }            // 等待所有线程执行完毕          try {              Thread.sleep(2000);          } catch (InterruptedException e) {              e.printStackTrace();          }            // 输出共享数据的最终值          System.out.println("Final shared data value: " + example.getSharedData());      }  }

这个示例中的同步块在JVM内部可能会使用轻量级锁(具体是否使用取决于JVM的实现和运行时环境)rzk28资讯网——每日最新资讯28at.com

在这个例子中,我们有一个sharedData变量,多个线程可能会同时访问它。我们使用synchronized块来确保每次只有一个线程能够修改sharedData。在JVM内部,这些synchronized块可能会使用轻量级锁来优化同步性能。rzk28资讯网——每日最新资讯28at.com

请注意,这个例子只是为了演示如何使用synchronized关键字,并不能保证JVM一定会使用轻量级锁。实际上,JVM可能会根据运行时的情况选择使用偏向锁、轻量级锁或重量级锁。rzk28资讯网——每日最新资讯28at.com

重量级锁

在Java中,重量级锁(Heavyweight Locking)是相对于轻量级锁而言的,它涉及到线程阻塞和操作系统级别的线程调度。当轻量级锁或偏向锁不足以解决线程间的竞争时,JVM会升级锁为重量级锁。rzk28资讯网——每日最新资讯28at.com

重量级锁通常是通过操作系统提供的互斥原语(如互斥量、信号量等)来实现的。当一个线程尝试获取已经被其他线程持有的重量级锁时,它会被阻塞(即挂起),直到持有锁的线程释放该锁。在阻塞期间,线程不会消耗CPU资源,但会导致上下文切换的开销,因为操作系统需要保存和恢复线程的上下文信息。rzk28资讯网——每日最新资讯28at.com

在Java中,synchronized关键字和java.util.concurrent.locks.ReentrantLock都可以导致重量级锁的使用,尤其是在高并发和激烈竞争的场景下。rzk28资讯网——每日最新资讯28at.com

我们来看看使用synchronized可能会涉及到重量级锁的代码:rzk28资讯网——每日最新资讯28at.com

public class HeavyweightLockExample {        private final Object lock = new Object();      private int counter;        public void increment() {          synchronized (lock) {              counter++;          }      }        public int getCounter() {          synchronized (lock) {              return counter;          }      }        public static void main(String[] args) {          HeavyweightLockExample example = new HeavyweightLockExample();            // 创建多个线程同时访问共享资源          for (int i = 0; i < 10; i++) {              new Thread(() -> {                  for (int j = 0; j < 10000; j++) {                      example.increment();                  }              }).start();          }            // 等待所有线程完成          try {              Thread.sleep(2000);          } catch (InterruptedException e) {              e.printStackTrace();          }            // 输出计数器的值          System.out.println("Final counter value: " + example.getCounter());      }  }

在这个示例中,多个线程同时访问counter变量,并使用synchronized块来确保每次只有一个线程能够修改它。如果线程间的竞争非常激烈,JVM可能会将synchronized块内部的锁升级为重量级锁。rzk28资讯网——每日最新资讯28at.com

我们说的是可能哈,毕竟内部操作还是由 JVM 具体来操控的。rzk28资讯网——每日最新资讯28at.com

我们再来看看这个ReentrantLock来实现:rzk28资讯网——每日最新资讯28at.com

import java.util.concurrent.locks.ReentrantLock;    public class ReentrantLockExample {        private final ReentrantLock lock = new ReentrantLock();      private int counter;        public void increment() {          lock.lock(); // 获取锁          try {              counter++;          } finally {              lock.unlock(); // 释放锁          }      }        public int getCounter() {          return counter;      }        public static void main(String[] args) {          // 类似上面的示例,创建线程并访问共享资源      }  }

在这个示例中,ReentrantLock被用来同步对counter变量的访问。如果锁竞争激烈,ReentrantLock内部可能会使用重量级锁。rzk28资讯网——每日最新资讯28at.com

需要注意的是,重量级锁的使用会带来较大的性能开销,因此在设计并发系统时应尽量通过减少锁竞争、使用更细粒度的锁、使用无锁数据结构等方式来避免重量级锁的使用。rzk28资讯网——每日最新资讯28at.com

偏向锁

在Java中,偏向锁(Biased Locking)是Java虚拟机(JVM)为了提高无竞争情况下的性能而引入的一种锁优化机制。它的基本思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程只需要检查Mark Word的锁标记位为偏向锁以及当前线程ID等于Mark Word的Thread ID即可,这样就省去了大量有关锁申请的操作。rzk28资讯网——每日最新资讯28at.com

他和轻量级锁和重量级锁一样,并不是直接通过Java代码来控制的,而是由JVM在运行时自动进行的。因此,你不能直接编写Java代码来显式地使用偏向锁。不过,你可以编写一个使用synchronized关键字的简单示例,JVM可能会自动将其优化为使用偏向锁(取决于JVM的实现和运行时的配置)。rzk28资讯网——每日最新资讯28at.com

示例代码:rzk28资讯网——每日最新资讯28at.com

public class BiasedLockingExample {        // 这个对象用作同步的锁      private final Object lock = new Object();            // 共享资源      private int sharedData;        // 使用synchronized关键字进行同步的方法      public synchronized void synchronizedMethod() {          sharedData++;      }        // 使用对象锁进行同步的方法      public void lockedMethod() {          synchronized (lock) {              sharedData += 2;          }      }        public static void main(String[] args) throws InterruptedException {          // 创建示例对象          BiasedLockingExample example = new BiasedLockingExample();            // 使用Lambda表达式和Stream API创建并启动多个线程          IntStream.range(0, 10).forEach(i -> {              new Thread(() -> {                  // 每个线程多次调用同步方法                  for (int j = 0; j < 10000; j++) {                      example.synchronizedMethod();                      example.lockedMethod();                  }              }).start();          });            // 让主线程睡眠一段时间,等待其他线程执行完毕          Thread.sleep(2000);            // 输出共享数据的最终值          System.out.println("Final sharedData value: " + example.sharedData);      }  }

在这个示例中,我们有一个BiasedLockingExample类,它有两个同步方法:synchronizedMethod和lockedMethod。synchronizedMethod是一个实例同步方法,它隐式地使用this作为锁对象。lockedMethod是一个使用显式对象锁的方法,它使用lock对象作为锁。rzk28资讯网——每日最新资讯28at.com

当多个线程调用这些方法时,JVM可能会观察到只有一个线程在反复获取同一个锁,并且没有其他线程竞争该锁。在这种情况下,JVM可能会将锁偏向到这个线程,以减少获取和释放锁的开销。rzk28资讯网——每日最新资讯28at.com

然而,请注意以下几点:rzk28资讯网——每日最新资讯28at.com

  • 偏向锁的使用是由JVM动态决定的,你不能强制JVM使用偏向锁。
  • 在高并发环境下,如果锁竞争激烈,偏向锁可能会被撤销并升级到更重的锁状态,如轻量级锁或重量级锁。
  • 偏向锁适用于锁被同一个线程多次获取的场景。如果锁被多个线程频繁地争用,偏向锁可能不是最优的选择。

由于偏向锁是透明的优化,因此你不需要在代码中做任何特殊的事情来利用它。只需编写正常的同步代码,让JVM来决定是否应用偏向锁优化。rzk28资讯网——每日最新资讯28at.com

分段锁

在Java中,"分段锁"并不是一个官方的术语,但它通常被用来描述一种并发控制策略,其中数据结构或资源被分成多个段,并且每个段都有自己的锁。这种策略的目的是提高并发性能,允许多个线程同时访问不同的段,而不会相互阻塞。rzk28资讯网——每日最新资讯28at.com

而在 Java 里面的经典例子则是ConcurrentHashMap,在早期的ConcurrentHashMap实现中,内部采用了一个称为Segment的类来表示哈希表的各个段,每个Segment对象都持有一个锁。这种设计允许多个线程同时读写哈希表的不同部分,而不会产生锁竞争,从而提高了并发性能。rzk28资讯网——每日最新资讯28at.com

然而,需要注意的是,从Java 8开始,ConcurrentHashMap的内部实现发生了重大变化。它不再使用Segment,而是采用了一种基于CAS(Compare-and-Swap)操作和Node数组的新设计,以及红黑树来处理哈希冲突。这种新设计提供了更高的并发性和更好的性能。尽管如此,"分段锁"这个概念仍然可以用来描述这种将数据结构分成多个可独立锁定的部分的通用策略。rzk28资讯网——每日最新资讯28at.com

我们看一个分段锁实现安全计数器的代码:rzk28资讯网——每日最新资讯28at.com

import java.util.concurrent.locks.Lock;  import java.util.concurrent.locks.ReentrantLock;    public class SegmentedCounter {      private final int size;      private final Lock[] locks;      private final int[] counters;        public SegmentedCounter(int size) {          this.size = size;          this.locks = new Lock[size];          this.counters = new int[size];          for (int i = 0; i < size; i++) {              locks[i] = new ReentrantLock();          }      }        public void increment(int index) {          locks[index].lock();          try {              counters[index]++;          } finally {              locks[index].unlock();          }      }        public int getValue(int index) {          locks[index].lock();          try {              return counters[index];          } finally {              locks[index].unlock();          }      }  }

在这个例子中,SegmentedCounter类有一个counters数组和一个locks数组。每个计数器都有一个与之对应的锁,这使得线程可以独立地更新不同的计数器,而不会相互干扰。当然,这个简单的例子并没有考虑一些高级的并发问题,比如锁的粒度选择、锁争用和公平性等问题。在实际应用中,你可能需要根据具体的需求和性能目标来调整设计。rzk28资讯网——每日最新资讯28at.com

所以,你学会了么?rzk28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-70469-0.htmlJava的ConcurrentHashMap是使用的分段锁?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 字节码增强技术,不止有 Java Proxy、 Cglib 和 Javassist 还有 Byte Buddy

下一篇: 十个Python编程小技巧

标签:
  • 热门焦点
  • 俄罗斯:将审查iPhone等外国公司设备 保数据安全

    iPhone和特斯拉都属于在各自领域领头羊的品牌,推出的产品也也都是数一数二的,但对于一些国家而言,它们的产品可靠性和安全性还是在限制范围内。近日,俄罗斯联邦通信、信息技术
  • 容量越大越不坏?24万块硬盘故障率报告公布 这些产品零故障

    8月5日消息,云存储服务商Backblaze发布了最新的硬盘故障率报告,年故障率有所上升。Backblaze发布的硬盘季度统计数据,其中包括故障率等重要方面。这些结
  • iPhone卖不动了!苹果股价创年内最大日跌幅:市值一夜蒸发万亿元

    8月5日消息,今天凌晨美股三大指数高开低走集体收跌,道指跌0.41%;纳指跌0.36%;标普500指数跌0.52%。热门科技股也都变化极大,其中苹果报181.99美元,跌4.8%,创
  • 一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 之家push系统迭代之路

    前言在这个信息爆炸的互联网时代,能够及时准确获取信息是当今社会要解决的关键问题之一。随着之家用户体量和内容规模的不断增大,传统的靠"主动拉"获取信息的方式已不能满足用
  • 腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • 猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对&ldquo;势&rdquo;的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  • 一条抖音4亿人围观 ! 这家MCN比无忧传媒还野

    作者:Hiu 来源:互联网品牌官01 擦边少女空降热搜,幕后推手曝光被网友誉为&ldquo;纯欲天花板&rdquo;的女网红井川里予,近期因为一组哥特风照片登上热搜,引发了一场互联网世界关于
  • 三星显示已开始为AR设备研发硅基LED微显示屏

    7月18日消息,据外媒报道,随着苹果首款头显产品Vision Pro在6月份正式推出,AR/VR/MR等头显产品也就将成为各大公司下一个重要的竞争领域,对显示屏这一关
Top