JAVA并发编程之锁应用

news2024/12/24 21:37:22

Java并发包是Java中提供的一个用于支持多线程编程的工具包。Java并发包提供了多种机制来控制线程的执行,保证线程的安全性和可靠性。下面我们将介绍Java并发包的使用方法,并给出示例。

synchronized

public class SynchronizedDemo {
​
  private int v;
  private static int a;
  private final Object lock = new Object();
​
  // 修饰非静态方法 对象锁
  public synchronized void add(int value) {
    v += value; // 临界区
  }
​
  public void sub(int value) {
    // 修饰局部代码块 对象锁
    synchronized (lock) {
      v -= value; // 临界区
    }
  }
​
  // 修饰静态方法 类锁
  public static synchronized void multi(int value) {
    a *= value; // 临界区
  }
​
  public static void div(int value) {
    // 修饰局部代码块 类锁
    synchronized (SynchronizedDemo.class) {
      a /= value; // 临界区
    }
  }
} 
复制代码

java编译器会在synchronized修饰的方法或代码块前后自动Lock,unlock。

synchronized修饰代码块,锁定是个obj对象,或者是一个类,sychronized(this.class)
synchronized修饰静态方法,锁定是当前类的class对象
synchronized修饰非静态方法,锁定的是当前实例对象this。

实现原理:

synchronized关键字底层使用的锁叫做Monitor锁。但是,我们无法直接创建和使用Monitor锁。Monitor锁是寄生存在的,每个对象都会拥有一个Monitor锁。如果我们想要使用一个新的Monitor锁,我们只需要使用一个新的对象,并在synchronized关键字后,附带声明要使用哪个对象的Monitor锁即可。

当使用sychronized修饰方法的时候,编译器只不过是在函数的flags中添加了ACC_SYNCHRONIZED标记而已,其他部分跟没有添加synchronized的函数的字节码相同。

当使用synchronized修饰局部代码块的时候,字节码通过monitorenter和monitorexit来标记synchronized的作用范围。但有两点需要再解释一下

synchronized关键字底层使用的锁叫做Monitor锁。但是,我们无法直接创建和使用Monitor锁。Monitor锁是寄生存在的,每个对象都会拥有一个Monitor锁,在字节码中,通过monitorenter前面的几行字节码来指定。
以下字节码中有两个monitorexit,添加第二个monitorexit的目的是为了在代码抛出异常时仍然能解锁。
monitor锁实现原理
synchronized在底层使用不同的锁来实现,重量级锁,轻量级锁,偏向锁等。

实际上,synchronized使用的重量级锁,就是前面提到的对象上的Monitor锁。JVM有不同的实现版本,因此,Monitor锁也有不同的实现方式。在Hotspot JVM实现中,Monitor锁对应的实现类为ObjectMonitor类。因为Hotspot JVM是用C++实现的,所以,ObjectMonitor也是用C++代码定义的。

Synchronized的缺点
无法判断获取锁的状态。
虽然会自动释放锁,但如果如果锁的那个方法执行时间较长就会一直占用着不去释放,不能让使用同一把锁的方法继续执行,影响程序的运行。不能设置超时。
当多个线程尝试获取锁时,未获取到锁的线程会不断的尝试获取锁,而不会发生中断,这样会造成性能消耗。
不能够实现公平锁

Lock和Condition

Java并发包中的 Lock 和 Condition 接口提供了一种更为灵活的同步机制。与 synchronized 不同的是,它们可以支持更为细粒度的锁控制,并且可以避免死锁问题。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockConditionExample {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private void method1() throws InterruptedException {
        lock.lock();
        try {
            System.out.println("method1 is running");
            condition.await();
            System.out.println("method1 is finished");
        } finally {
            lock.unlock();
        }
    }

    private void method2() {
        lock.lock();
        try {
            System.out.println("method2 is running");
            condition.signal();
            System.out.println("method2 is finished");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockConditionExample example = new LockConditionExample();
        Thread thread1 = new Thread(() -> {
            try {
                example.method1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread2 = new Thread(example::method2);
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
    }
}

Lock 和 Condition 接口用于控制 method1 和 method2 方法的执行顺序。其中 method1 方法会先获取锁并进入等待状态,而 method2 方法会在一段时间后唤醒 method1 方法并释放锁。这样就可以保证 method1 方法先执行。

Semaphore

Semaphore 是一个计数信号量,用于控制同时访问某个资源的线程数。可以将 Semaphore 看作是一种计数器,每当有线程访问该资源时,计数器的值减一;当计数器的值为零时,其他线程需要等待,直到有线程释放该资源。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

public class IncrementThread extends Thread {
    private Counter counter;

    public IncrementThread(Counter counter) {
        this.counter = counter;
    }

    public void run() {
        for (int i = 0; i < 1000000; i++) {
            counter.increment();
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new IncrementThread(counter);
        Thread t2 = new IncrementThread(counter);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Count: " + counter.getCount());
    }
}

区别:

Synchronized 被称为隐式锁,也叫 JVM 锁,因为它锁的持有和释放都是隐式的,无须开发者干预。Java 1.5 引入新的锁机制,其中锁的实现基于 Lock 接口:

public interface Lock {
    // 加锁
    void lock();
    // 解锁
    void unlock();
    // 可中断获取锁,获取锁时可响应中断操作
    void lockInterruptibly() throws InterruptedException;
    // 尝试非阻塞获取锁,能够获得返回true,否则返回false
    boolean tryLock();
    // 根据时间尝试获取锁,能够获得返回true,否则返回false
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 获取等待通知组件,该组件与当前锁绑定
    Condition newCondition();
}
 

ReentrantLock 锁基于 AQS 队列同步器实现,全称 AbstractQueuedSynchronizer,其中它的抽象类如下:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
    // 0表示锁未被占用,1表示已占用
    private volatile int state;
    // 指向同步队列队头
    private transient volatile Node head;
    // 指向同步队列队尾
    private transient volatile Node tail;
    // 其余属性省略
}

公平锁和非公平锁的差别在于:

公平锁:先请求锁的线程会优先获取到锁,原理是所有需要获得锁的线程都会进入到队列中,队列的特点是先进先出,先进入的请求线程会在头部,后进入的请求线程都会在队列的尾部。

非公平锁:则不会按照线程请求获得锁的先后顺序,会立马进行一次获取锁的请求操作。

synchronized:属于独占锁、悲观锁、可重入锁、非公平锁

ReentrantLock:继承了Lock类,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

Lock:Java中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/593457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为云认证有什么?考试难不难?

最近几年华为云的市场占比越来越大&#xff0c;逐渐占据了我们生活中的方方面面&#xff0c;而且很多政企单位&#xff0c;也选择华为云作为合作伙伴&#xff0c;因此市场上也需要越来越多的华为云人才&#xff0c;早在几年前&#xff0c;华为云就已经推出了自己的人才认证系统…

条件随机场模型

条件随机场模型&#xff08;Conditional Random Fields, CRF&#xff09; 条件随机场是给定一组输入随机变量条件下&#xff0c;另一组输出随机变量的条件概率分布模型&#xff0c;其特点是假设输出随机变量构成马尔可夫随机场。线性链条件随机场&#xff0c;是输入序列对输出…

用 JavaScript 对抗 DDOS 攻击

继续趣事分享。 上回聊到了大学里用一根网线发起攻击&#xff0c;今天接着往后讲。 不过这次讲的正好相反 —— 不是攻击&#xff0c;而是防御。一个奇葩防火墙的开发经历。 第二学期大家都带了电脑&#xff0c;于是可以用更高端的方法断网了。但设备先进反而没有了 GEEK 的…

第十七章行为性模式—状态模式

文章目录 状态模式解决的问题反例 结构实例存在的问题使用场景 状态模式与策略模式的区别 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。行为型…

Java中常见转换-数组与list互转、驼峰下划线互转、Map转Map、List转Map、进制转换的多种方式

场景 Java中数组与List互转的几种方式 数组转List 1、最简单的方式,Arrays.asList(array); 创建的是不可变列表&#xff0c;不能删除和新增元素 String[] array new String[]{"a","b"};List<String> stringList Arrays.asList(array);System.ou…

嵌入式和单片机

凡是从事信息技术相关工作的&#xff0c;一定都听说过嵌入式和单片机。 大家都知道&#xff0c;这两个名词&#xff0c;和硬件系统有着非常密切的关系。 但是&#xff0c;如果要问具体什么是嵌入式&#xff0c;什么是单片机&#xff0c;它们之间究竟有什么区别&#xff0c;我…

【NovelAI 小说SD批量生成 文生图】Web版环境配置和使用方法

样片&#xff1a; 【样品】《谜影之夜》文生图全自动版SD一键成片 操作演示&#xff1a; 【txt2video web】携带漫画插件的Web版AI小说生成工具无声演示版 操作口述教程&#xff1a; 【NovelAI】携带漫画插件的Web版AI小说生成视频工具 该文章面向购买脚本的付费用户&#xff0…

钓鱼网站也在使用https加密,如何识别钓鱼网站?

信息安全是一个庞大的领域&#xff0c;其中涉及到很多知识点&#xff0c;但是大多公司都对其没有提及足够的重视&#xff0c;希望随着国内对于安全的越来越重视&#xff0c;更多的公司也能在信息安全领域投入越来越多的注意。 安装SSL证书是为了对数据进行加密传输&#xff0c…

轻松学会食堂管理,就这么简单!

随着科技进步和生活水平的不断提高&#xff0c;人们对于餐饮消费的需求也逐渐变得多样化和个性化。 高校食堂现状分析 01.信息化水平低&#xff0c;学校管理难&#xff0c;无法精准就餐&#xff1b; 02用户满意度低&#xff0c;学生取餐环节效率低&#xff1b; 03.管理效率低…

【Redis】浅谈Redis-集群(Cluster)

文章目录 前言1、集群实现1.1 创建cluster目录&#xff0c;并将redis.conf复制到该文件夹1.2 复制redis.conf&#xff0c;并进行配置1.3 启动redis&#xff0c;查看启动状态1.4 合成集群1.5 查看集群1.6 集群读写操作 2、SpringBoot整合redis集群2.1 引入包2.2 设置配置2.3 使用…

C++服务器框架开发5——日志系统logAppender/IO类“3种stream”/双感叹号

该专栏记录了在学习一个开发项目的过程中遇到的疑惑和问题。 其教学视频见&#xff1a;[C高级教程]从零开始开发服务器框架(sylar) 上一篇&#xff1a;C服务器框架开发4——日志系统logger/.cpp与.cc C服务器框架开发5——日志系统logAppender/IO类“3种stream”/双感叹号 目前…

2022年天府杯全国大学生数学建模竞赛C题环境保护与绿色经济解题全过程文档及程序

2022年天府杯全国大学生数学建模竞赛 C题 环境保护与绿色经济 原题再现&#xff1a; “节能减排”和发展绿色经济是国家一项重大战略规划。“绿水青山就是金山银山”。环境的保护对国民经济的发展有着重要意义。随着工业化进程的加快&#xff0c;环境保护方面的问题已经刻不容…

ClickHouse进阶|如何自研一款企业级高性能网关组件?

使用原生ClickHouse集群进行节点数据查询和写入时&#xff0c;离不开第三方开源网关组件chproxy支持。但由于chproxy缺少TCP协议支持&#xff0c;导致性能、查询能力等受限。这也成为困扰众多ClickHouse开发者的一大难题。那么&#xff0c;究竟应该如何突破&#xff1f;本文将揭…

chatgpt赋能python:Python中撤销的快捷键

Python中撤销的快捷键 在编程中&#xff0c;我们经常需要进行调试&#xff0c;不可避免地会出现一些错误&#xff0c;这时候撤销 (Undo) 功能就显得尤为重要。在 Python 中&#xff0c;我们可以使用一些快捷键来快速撤销&#xff0c;本文将会介绍这些快捷键的使用以及使用它们…

chatgpt赋能python:Python中的字符提取:从基础到高级

Python中的字符提取&#xff1a;从基础到高级 在使用Python进行文本处理和数据挖掘时&#xff0c;我们经常需要从字符串中提取特定的字符或子串。本文将介绍Python中的常用字符串提取方法&#xff0c;包括基础的字符串操作、正则表达式和第三方库等高级方法。 基础字符串操作…

二叉树中堆的实现

完全二叉树 满二叉树&#xff1a;二叉树每个节点的度都达到最大值&#xff08;2&#xff09;&#xff0c;由此可有等比求和计算出节点总数&#xff1a;2^k-1 完全二叉树&#xff1a;除了最后一层。前面节点的度都满了&#xff0c;最后一层可以不满&#xff0c;但是必须从左至右…

用时序数据库 DolphinDB 实现地震波形的分析预警

1. 绪论 波形数据的存储与实时流处理是地震预警、地震速报、地震烈度速报、震源机制解等数字地震台网综合处理系统的前提&#xff0c;合理的存储方案与高效的实时流处理架构能极大地节约存储成本、降低响应延时、方便震源分析。 本篇教程会为有该方面需求的客户提供一个基于 …

如何在不丢失格式的情况下将 Excel 转换为 PDF?

Microsoft Excel 因其专业的数据或信息显示能力而被广泛采用。但是&#xff0c;它在兼容性上有一个恼人的缺点。 首先&#xff0c;Excel只能在Windows和MacOS上使用&#xff0c;Linux或其他操作系统的用户将无法使用&#xff1b;其次&#xff0c;即使您有一个可以打开和阅读 E…

PERMA幸福模型

PERMA幸福模型 由“积极心理学之父”马丁塞利格曼&#xff08;Martin Seligman&#xff09;在《持续的幸福 》一书中提出。 模型介绍 PERMA指的是创造充实、快乐和有意义的生活所必需的五大要素&#xff0c;它们是: 正向情绪Positive emotions 积极情绪是快乐人生的一个重要…

三种不同的存储芯片性能比较

为了进行性能比较&#xff0c;使用了三种不同的存储芯片&#xff0c;即Everspin EM064LX 64Mib STT‐MRAM、Micron MT25Q 128Mib NOR闪存和Micron MT29F 1Gib SLC NAND闪存。 该基准测试在STM32H755ZI上运行&#xff0c;Cortex‐M7内核的时钟频率为240MHz。Quad SPI总线用于1‐…