synchronized和Lock(ReentrantLock)及二者区别

news2024/11/16 13:53:40

synchronized

是用于实现线程同步的关键字。它提供了两种主要的方式来保证多个线程访问共享资源时的互斥性和可见性:同步块和同步方法。

  1. 同步块

同步块允许你指定一个对象作为锁,并保护一段代码区域。这样,同一时刻只有一个线程可以执行这段被同步的代码。基本语法如下:

synchronized (lockObject) {
    // lockObject 是一个对象引用,通常是某个实例变量或者 this 关键字,也可以是一个局部变量。
}

示例

设我们有两个线程需要访问一个共享的 StringBuilder 实例,并对其进行修改:

public class SyncExample {
    private final StringBuilder sharedString = new StringBuilder();

    public void append(String text) {
        synchronized (sharedString) {
            sharedString.append(text);
            System.out.println(sharedString);
        }
    }
}

这个示例使用 sharedString 作为锁对象。当一个线程进入同步块时,它会获取 sharedString 的锁,其他线程必须等待这个线程释放锁才能进入同步块。

  1. 同步方法

同步方法是指在方法声明前加上 synchronized 关键字的方法。当一个线程调用一个对象的同步方法时,它会自动获取该对象的锁。如果该对象的另一个同步方法已经被另一个线程调用,那么调用当前方法的线程将被阻塞,直到另一个线程释放锁。

示例

public class SyncExample {
    private final StringBuilder sharedString = new StringBuilder();

    public synchronized void append(String text) {
        sharedString.append(text);
        System.out.println(sharedString);
    }
}

在这个例子中,append 方法是同步的。当一个线程调用 append 方法时,它会自动获取 SyncExample 实例的锁。这意味着如果一个线程正在执行 append 方法,其他线程将不能调用同一个实例上的任何其他同步方法,也不能调用同一个实例上的 append 方法。

Lock(锁)

Lock锁也称同步锁,用法特性如下:

  1. 公平锁和非公平锁

它们之间的主要区别在于线程获取锁的顺序
非公平锁的优点在于吞吐量比公平锁要高,因为非公平锁更容易产生线程切换,从而增加系统的并发性。默认为非公平锁。在这里插入图片描述

公平锁 (Fair Lock)

公平锁遵循先进先出 (FIFO) 的原则,即线程获取锁的顺序与它们请求锁的顺序相同。这意味着如果一个线程先于其他线程请求锁,那么它就有优先权获得锁。

非公平锁 (Non-Fair Lock)

非公平锁不保证线程获取锁的顺序,它允许一个后来请求锁的线程有可能比先请求锁的线程更快地获取到锁。这是因为非公平锁在尝试获取锁时可能会偏向于当前持有锁的线程

  1. 锁定和解锁

使用lock()方法获取锁,使用unlock()方法释放锁。务必确保每次调用 lock() 之后都有对应的 unlock() 调用。

  1. 可重入性

ReentrantLock 支持可重入性,这意味着一个已经持有锁的线程可以再次获取锁,而不会导致死锁。每次获取锁都会增加锁的计数,每次释放锁都会减少计数。只有当计数为零时,其他线程才能获取锁。

ReentrantLock lock = new ReentrantLock();

public void someMethod() {
    lock.lock();
    try {
        System.out.println("First lock acquired.");

        lock.lock();
        try {
            System.out.println("Second lock acquired.");
        } finally {
            lock.unlock(); // 释放第二个锁
        }

        System.out.println("Still inside the first lock.");
    } finally {
        lock.unlock(); // 释放第一个锁
    }
}

例如

当一个方法需要在其内部递归调用自身时,可重入性可以确保线程在递归过程中仍然能够获取锁。

public class RecursiveMethod {
    private final ReentrantLock lock = new ReentrantLock();

    public void recursiveMethod(int n) {
        lock.lock();
        try {
            if (n > 0) {
                System.out.println("Recursive call " + n);
                recursiveMethod(n - 1); // 递归调用
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        RecursiveMethod method = new RecursiveMethod();
        method.recursiveMethod(5);
    }
}
  1. 获取锁超时

如果需要在等待锁的过程中设置超时,可以使用tryLock()方法。tryLock() 可以带有或不带超时时间。用于尝试获取锁而不阻塞。如果锁可用,则获取锁并返回 true;如果锁不可用,则不获取锁并返回 false。这使得你可以在不需要等待锁的情况下检查锁的状态,并根据锁是否可用采取不同的行动。

使用 tryLock() 的基本形式

public class TryLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        if (lock.tryLock()) {
            try {
                // 临界区代码
                System.out.println("Inside the critical section.");
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("Could not acquire the lock.");
        }
    }

    public static void main(String[] args) {
        TryLockExample example = new TryLockExample();
        example.performTask();
    }
}

使用 tryLock() 的超时形式

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

public class TryLockWithTimeoutExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        if (lock.tryLock(10, TimeUnit.MILLISECONDS)) {
            try {
                // 临界区代码
                System.out.println("Inside the critical section.");
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("Could not acquire the lock within the timeout.");
        }
    }

    public static void main(String[] args) {
        TryLockWithTimeoutExample example = new TryLockWithTimeoutExample();
        example.performTask();
    }
}

tryLock(10, TimeUnit.MILLISECONDS) 表示尝试获取锁,等待时间最长为 10 毫秒。如果在这段时间内锁可用,则获取锁并执行临界区代码;否则返回 false,并在控制台上输出相应的消息。

  1. 响应中断

ReentrantLock 提供了lockInterruptibly()方法,它在获取锁时允许线程在等待锁的过程中响应中断信号。如果锁不可用,线程将阻塞并等待锁变得可用。如果在此期间线程被中断,lockInterruptibly() 方法将抛出 InterruptedException。

interrupt()用于向线程发送中断信号。
Thread.isInterrupted()来检查线程是否被中断。

import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptiblyExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        try {
            lock.lockInterruptibly();
            try {
                // 临界区代码
                System.out.println("Inside the critical section.");
                Thread.sleep(1000);
                System.out.println("Leaving the critical section.");
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            // 捕获中断异常
            System.out.println("Thread was interrupted while waiting for the lock.");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockInterruptiblyExample example = new LockInterruptiblyExample();
        Thread thread = new Thread(example::performTask);
        thread.start();

        // 等待一段时间后中断线程
        Thread.sleep(500);
        thread.interrupt();

        // 等待线程完成
        thread.join();
    }
}

输出:
Inside the critical section.
Thread was interrupted while waiting for the lock.

synchronized与Lock的对比

synchronized

特点

  • 语法简洁:使用关键字 synchronized 来声明同步块或同步方法。
  • 自动释放锁:当线程离开同步块或方法时,锁会自动释放。
  • 死锁检测:JVM 自动处理锁的获取和释放,可以防止锁没有被释放的情况。
  • 支持重入
  • 不支持超时:无法指定获取锁的超时时间。
  • 不支持尝试锁:无法尝试获取锁而不阻塞。

Lock

特点

  • 显式锁:必须显式地获取和释放锁。
  • 可中断的锁:支持通过 lockInterruptibly() 方法获取锁,允许响应中断。
  • 超时锁:支持通过 tryLock() 方法尝试获取锁,并可以选择性地指定超时时间。
  • 公平性和非公平性:ReentrantLock 支持公平锁和非公平锁。
  • 锁的粒度控制:可以更细粒度地控制锁的获取和释放。
  • 死锁检测:如果不正确地使用,可能导致死锁。
  • 支持重入

对比总结

使用场景:

  • synchronized 更适合简单的同步需求,代码更简洁。
  • Lock 适用于更复杂的同步需求,提供了更多的控制选项。
性能:
  • 在 JDK 1.6 之后,synchronized 的性能得到了显著提升,对于大部分场景来说,其性能与 ReentrantLock 相当。
  • 对于更高级的锁控制需求,如尝试锁或可中断锁,ReentrantLock 提供了更好的解决方案。
灵活性:

Lock 提供了更高级的功能,如尝试获取锁、超时获取锁等,因此在灵活性方面优于 synchronized。

死锁检测:
  • synchronized 由 JVM 自动管理,因此更安全。
  • Lock 需要程序员手动管理,容易出错,可能导致死锁。

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

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

相关文章

ChatGPT 为什么不建议关闭 MySQL 严格模式?

社区王牌专栏《一问一实验:AI 版》全新改版归来,得到了新老读者们的关注。其中不乏对 ChatDBA 感兴趣的读者前来咨询,表达了想试用体验 ChatDBA 的意愿,对此我们表示感谢 🤟。 目前,ChatDBA 还在最后的准备…

记录一次 npm ERR! cb() never called! 解决过程

gitlab cicd过程,使用docker部署Vue3前端项目,报错如下: 针对 npm ERR! cb() never called! 这个报错,网上有很多解决方案,大都是清空缓存,重新运行npm 之类的。笔者全都试过,无法解决问题。笔者…

linux,docker查看资源消耗总结

在linux和docker中我们将一个程序运行到后台,之后我们想查看它的运行状态,对于服务器的资源消耗等等 1.linux查看进程 ps aux | grep python ps aux:列出所有正在运行的进程。grep python:过滤出包含 python 的进程 2.linux查…

springCloud集成activiti5.22.0流程引擎(分支)

springCloud集成activiti5.22.0流程引擎 点关注不迷路,欢迎再访! 精简博客内容,尽量已行业术语来分享。 努力做到对每一位认可自己的读者负责。 帮助别人的同时更是丰富自己的良机。 文章目录 springCloud集成activiti5.22.0流程引擎一.Sprin…

你知道家电的保质期吗?

家人们,你们有关注过家里的电器用了多少年了吗? “家电不坏,就能一直用。” “坏了修一修,一样能用。” 很多家长都有这样的想法,家里的电器即使出了故障,修一修也就继续用了。 其实,家电也…

打造智能化直播商城平台:AI与大数据在平台开发中的应用

在当今竞争激烈的电商市场中,直播商城平台已经成为品牌和商家实现差异化竞争的重要工具。随着人工智能(AI)和大数据技术的不断进步,智能化直播商城平台的开发成为了行业的新趋势。这些技术不仅可以优化用户体验,还能提…

AI革新体育:IBM携手USTA升级美国公开赛观赛体验

IBM和美国网球协会(USTA)合作,在2024年美国网球公开赛中引入了创新的AI技术,为观众和选手带来全新的体验。8月19日赛事开幕,IBM的watsonx平台将推出多项新功能,增强三周赛事的互动性。 喜好儿网 IBM的Gra…

Prometheus:pushgateway使用

1 项目目标 (1)熟练部署pushgateway (2)使用api增删改查数据 (3)使用python Client SDK Push数据到pushgateway 2.1 规划节点 主机名 主机IP 节点规划 prome-master01 10.0.1.10 服务端 prome-no…

探索PyUSB:Python与USB设备的桥梁

文章目录 探索PyUSB:Python与USB设备的桥梁背景:为何选择PyUSB?什么是PyUSB?如何安装PyUSB?简单的库函数使用方法场景应用常见问题与解决方案总结 探索PyUSB:Python与USB设备的桥梁 背景:为何选…

GD32F470 FREERTOS + lwip UDP丢包问题解决

现象:使用GD32F470Z评估板已经官方FreeRTOSUDP例程测试,使用上位机UDP测试工具,连续收发UDP数据包,每发送65535次数据,第65536包数据就会丢。如下图所示: 测试了很多次,都是在65536的时候停了&a…

NVR方案背景与产品介绍与构建一套完整的NVR产品解决方案

一、NVR和DVR 在视频监控领域,DVR和NVR是两种常用的录像技术。它们在系统结构、视频处理、存储和访问方式等方面存在明显的区别。,但都在视频监控中扮演着重要的角色。首先来了解它们的区别和特点,这有助于在选择合适的设备时做出明智的决策…

HDMI切换器(2进1,1进2,三切1)介绍

目录 HDMI介绍: 二进一出HDMI切换器: 通俗的解释: 一进二出HDMI切换器: 通俗解释: HDMI1进2和2进1的区别: 三进1出HDMI切换器: 通俗的解释: HDMI介绍: HDMI描述全称高清多媒体接口(High Definition Multimedia Interface&…

太阳能光伏气象站——助力光伏发电

在光伏产业蓬勃发展的今天,‌太阳能光伏气象站作为专为光伏发电站打造的环境监测系统,‌其重要性日益凸显。‌它不仅是电站运维优化、‌智能控制的关键环节,‌更是提高发电效率、‌保障光伏电站稳定运行的重要工具。‌ 首先,‌太阳…

Linux--传输层协议TCP

目录 1.理解TCP的部分字段 2.TCP的策略以及其它报头 确认应答(ACK)机制​编辑 超时重传机制 连接管理机制 建立连接为什么要三次握手? 为什么要四次挥手? 验证两种状态,CLOSE_WAIT(不关闭文件fd即可)和TIME_WA…

langchian 批次调用 prompt

目录 基础不使用批次 batch 批次调用 关于 langchian 额一些应用,可以查看案例: GitHub - 5zjk5/prompt-engineering: prompt 工程项目案例 基础不使用批次 from dotenv import load_dotenv import time import os from langchain_core.prompts imp…

【JUC】07-死锁

1. 死锁 死锁指的是两个或以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。 // 死锁代码 public class DeadLockDemo {public static void main(String[] args) {final Object objectA new Object();final Object objectB new Object();new Threa…

【Linux 驱动】IMX6ULL gpio驱动

1. 概述 如果 pinctrl子系统将一个 PIN 复用为 GPIO 的话,那么接下来要用到 gpio 子系统了。gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,设置读取 GPIO 的值等。 gpio 子系统的主…

kettle-spoon界面空白

点击spoon的connect、save、打开资源库等等,出现以下界面空白,已排查IE11的问题。 解决办法:清除kettle的配置文件,包括:Data Integration/.kettle、C:\Users\XXX.kettle等所有配置文件。

【机器学习】YOLO 关闭控制台推理日志

问题背景 使用 YOLO v8 推理时,每次推理都会在控制台输出日志,大批量推理时会把自己打印的日志给冲掉,现想关闭 YOLO v8 的推理日志。 解决方案 方案一: 在预测接口的参数列表里加上 verboseFalse 即可关闭控制台输出日志。 m…

全志 HDMI 显示亮度低

一、问题描述 全志T527在适配HDMI,让HDMI作为主显示时,出现亮度太低的问题 二、解决办法 1、调整uboot参数,显示720P画面 vi device/config/chips/t527/configs/sany_v7/uboot-board.dts 在T527中有显示相关的接口,enhance 该接口用于设置图像的亮度/对比度/饱和度/边缘…