JAVA并发编程之锁应用

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中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自学网络安全,一般人我劝你还是算了吧

学前感言: 我为什么会这样说&#xff0c;要一般人自学网络安全就算了&#xff0c;因为我不是一般人 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发 .3.有时多 google,baidu,我们往往都…

termux-x11教程

小粉丝已经求稿两个星期了&#xff0c;不写是不行了。 termux-x11 是Termux的一个图形化项目&#xff0c;官方是这么介绍的。 A Termux add-on app providing Android frontend for Xwayland.安装工具 我们需要在Termux和安卓系统上安装工具以成功的运行程序。 x11-repo&am…

使用canvas给图片添加水印

上接文章“图片处理” canvas元素其实就是一个画布&#xff0c;我们可以很方便地绘制一些文字、线条、图形等&#xff0c;它也可以将一个img标签里渲染的图片画在画布上。 我们在上传文件到后端的时候&#xff0c;使用input标签读取用户本地文件后得到的其实是一个Blob对象&a…

HNU-电路与电子学-小班3

第三次讨论 1 、直接用晶体管而不是逻辑门实现异或门&#xff0c;并解释这个电路是如何工作的。 &#xff08;6个 MOS 管构成&#xff09; 2 、通信双方约定采用 7 位海明码进行数据传输。请为发送方设计海明码校验位 生成电路&#xff0c;采用功能块和逻辑门为接收方设计海…

ISO_IEC_7816-3

介绍 ISO/IEC 7816 是一系列标准&#xff0c;规定了集成电路卡和此类卡的使用 互换。 这些卡是用于在外部世界和卡中的集成电路之间协商的信息交换的识别卡。 作为信息交换的结果&#xff0c;卡传递信息&#xff08;计算结果、存储的数据&#xff09;和/或修改其内容&#xff0…

路径规划算法:基于果蝇优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于果蝇优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于果蝇优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法果蝇…

2023全国酒店数据

数据内容字段结构 hotel_id int(11) NOT NULL, name varchar(100) DEFAULT NULL, name_en varchar(100) DEFAULT NULL, short_name varchar(100) DEFAULT NULL, province varchar(20) DEFAULT NULL, city_id int(11) DEFAULT NULL, city varchar(20…

Android12之源码手动生成aidl对应java/cpp/ndk/rust服务(一百五十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

博客系统(ssm版本)

在前面的文章中给大家介绍过博客系统的servlet版本&#xff0c;但是servlet的技术非常的老旧&#xff0c;我们在企业中用的都是springboot相关的框架&#xff0c;本章内容就是讲述如何一步一步的利用ssm的技术来实现博客系统。 目录 前期配置 创建数据库 配置文件 公共文件…

【JavaSE】Java基础语法(五):数组详解

文章目录 &#x1f378;1.1 数组介绍&#x1f378;1.2 数组的动态初始化1.2.1 什么是动态初始化1.2.2 动态初始化格式&#x1f378;1.3 数组元素访问1.3.1 什么是索引1.3.2 访问数组元素格式1.3.3 示例代码 &#x1f378;1.4 内存分配1.4.1 内存概述1.4.2 java中的内存分配 &am…

【Java-10】深入浅出线程安全、死锁、状态、通讯、线程池

主要内容 线程安全线程死锁线程的状态线程间通讯线程池 1 线程安全 1.1 线程安全产生的原因 多个线程在对共享数据进行读改写的时候&#xff0c;可能导致的数据错乱就是线程的安全问题了 问题出现的原因 : 多个线程在对共享数据进行读改写的时候&#xff0c;可能导致的数据…

第五十天学习记录:C语言进阶:位段

位段 什么是位段 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1、位段的成员可以是int,unsigned int或signed int。 2、位段的成员名后边有一个冒号和一个数字。 #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//位段-二进制位 struct A {int …

GoWeb -- gin框架的入门和使用(2)

前言 书接上回&#xff0c;在gin的框架使用中&#xff0c;还有着许多方法以及它们的作用&#xff0c;本篇博客将会接着上次的内容继续记录本人在学习gin框架时的思路和笔记。 如果还没有看过上篇博客的可以点此跳转。 map参数 请求url&#xff1a; http://localhost:8080/us…

什么是IPAM?如何使用IPAM来管理IP地址和DHCP?

在计算机网络中&#xff0c;IPAM&#xff08;IP Address Management&#xff09;是一种用于管理IP地址和DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;的工具或系统。IPAM旨在简化和集中管理IP地址分配、子网划分和DHCP配置等任务。本文将详细介绍IPAM的概…

奇偶分频电路

目录 偶数分频 寄存器级联法 计数器法 奇数分频 不满足50%占空比 50%占空比 偶数分频 寄存器级联法 寄存器级联法能实现2^N的偶数分频&#xff0c;具体做法是采用寄存器结构的电路&#xff0c;每当时钟上升沿到来的时候对输出结果进行翻转&#xff0c;以此来实现偶数分…

chatgpt赋能python:Python中日期转换:从字符串到日期对象

Python中日期转换&#xff1a;从字符串到日期对象 作为一个经验丰富的Python工程师&#xff0c;日期转换在我的日常编码工作中经常遇到。Python提供了一些内置函数和模块&#xff0c;可以将字符串转换为日期对象或将日期对象格式化为特定的字符串。本篇文章将带您深入了解Pyth…

【JavaSE】Java基础语法(二十二):包装类

文章目录 1. 基本类型包装类2. Integer类3. 自动拆箱和自动装箱4. int和String类型的相互转换 1. 基本类型包装类 基本类型包装类的作用 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据常用的操作之一&#xff1a;用于基本数据类型与字符串之间的…

黑马Redis视频教程实战篇(三)

目录 一、优惠券秒杀 1.1 全局唯一ID 1.2 Redis实现全局唯一ID 1.3 添加优惠卷 1.4 实现秒杀下单 1.5 库存超卖问题分析 1.6 代码实现乐观锁解决超卖问题 1.7 优惠券秒杀-一人一单 1.8 集群环境下的并发问题 二、分布式锁 2.1 基本原理和实现方式对比 2.2 Redis分布…

【计算思维题】少儿编程 蓝桥杯青少组计算思维真题及详细解析第6套

少儿编程 蓝桥杯青少组计算思维真题及详细解析第6套 1、兰兰有一些数字卡片,从 1 到 100 的数字都有,她拿出几张数字卡片按照一定顺序摆放。想一想,第 5 张卡片应该是 A、11 B、12 C、13 D、14 答案:C 考点分析:主要考查小朋友们的观察能力和数学推理能力,从给定的图…

交换机的4种网络结构方式:级联方式、堆叠方式、端口聚合方式、分层方式

交换机是计算机网络中重要的网络设备之一&#xff0c;用于实现局域网&#xff08;LAN&#xff09;内部的数据转发和通信。交换机可以采用不同的网络结构方式来满足不同的网络需求和拓扑结构。本文将详细介绍交换机的四种网络结构方式&#xff1a;级联方式、堆叠方式、端口聚合方…