首页 > 编程语言 >Java synchronized 、ReentrantLock和Semaphore

Java synchronized 、ReentrantLock和Semaphore

时间:2023-12-08 13:45:35浏览次数:33  
标签:Java Thread synchronized ReentrantLock 线程 访问共享 Semaphore

synchronized

在Java中,使用synchronized关键字可以实现对代码块或方法的同步访问,以确保多个线程不会同时访问共享资源。当一个线程获取了对象的锁(即进入了synchronized代码块),其他线程如果也希望获取该对象的锁,它们将被阻塞,直到拥有锁的线程执行完毕并释放锁。

因此,在某种意义上,使用synchronized关键字可以导致线程阻塞。这是因为当一个线程持有锁时,其他线程在尝试获得相同锁时将会被阻塞,直到持有锁的线程释放锁。

然而,需要注意的是,线程阻塞并不是synchronized关键字的本质特性,而是由于锁的竞争和获取导致的一种现象。synchronized关键字的作用是确保对共享资源的安全访问,而阻塞是由于多个线程对锁的竞争导致的。

在实际编码中,应该谨慎地使用synchronized关键字,避免出现过多的锁竞争,从而导致性能问题和线程阻塞。同时,还可以考虑使用更灵活的并发控制工具,如ReentrantLock、Semaphore等,来避免一些synchronized可能引起的问题。

ReentrantLock

ReentrantLock是Java中的一个可重入锁(Reentrant Lock),它提供了与synchronized关键字类似的功能,但更加灵活和扩展。

与synchronized关键字不同,ReentrantLock允许线程多次获取同一个锁,而不会导致死锁。它使用计数器来跟踪锁的持有次数,每次获取锁时计数器加一,释放锁时计数器减一。只有当计数器为零时,其他线程才能获取该锁。

下面是一个简单的示例,演示了如何使用ReentrantLock来控制并发访问:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                lock.lock(); // 获取锁
                // 访问共享资源的代码
                System.out.println("Thread " + Thread.currentThread().getId() + " is accessing the shared resource");
                Thread.sleep(2000); // 模拟访问共享资源的耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); // 释放锁
            }
        };

        // 创建多个线程并启动
        for (int i = 0; i < 5; i++) {
            new Thread(task).start();
        }
    }
}

在这个示例中,我们创建了一个ReentrantLock实例,并在多个线程中使用lock()方法获取锁,并在访问共享资源后使用unlock()方法释放锁。

与synchronized关键字相比,ReentrantLock提供了更多的功能,比如可定时的、可中断的锁等待机制、公平性等。但需要注意的是,使用ReentrantLock需要显式地调用lock()和unlock()方法来控制锁的获取和释放,确保在异常情况下锁能够被正确释放,否则可能导致死锁。

总的来说,ReentrantLock是一种强大而灵活的锁实现,可以在需要更复杂的并发控制时使用,提供了比synchronized更多的功能选项。

Semaphore

Semaphore是Java中用于控制并发访问的工具之一,它可以限制对共享资源的访问数量。与synchronized关键字不同,Semaphore允许多个线程同时访问共享资源,但是通过控制许可证的数量来限制并发访问的线程数。

Semaphore维护了一定数量的许可证(permits),线程在访问共享资源之前必须先获得许可证,如果许可证数量耗尽,其他线程将被阻塞直到有线程释放许可证。

下面是一个简单的示例,演示了如何使用Semaphore来控制并发访问:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2); // 创建一个初始许可证数量为2的Semaphore

        Runnable task = () -> {
            try {
                semaphore.acquire(); // 获取许可证
                // 访问共享资源的代码
                System.out.println("Thread " + Thread.currentThread().getId() + " is accessing the shared resource");
                Thread.sleep(2000); // 模拟访问共享资源的耗时操作
                semaphore.release(); // 释放许可证
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        // 创建多个线程并启动
        for (int i = 0; i < 5; i++) {
            new Thread(task).start();
        }
    }
}

在这个示例中,我们创建了一个初始许可证数量为2的Semaphore,然后创建了5个线程尝试访问共享资源。由于Semaphore的许可证数量限制为2,只有两个线程可以同时获得许可证并访问共享资源,其他线程将会被阻塞。

总的来说,Semaphore提供了比synchronized更灵活、更细粒度的并发访问控制,可以帮助我们更好地管理共享资源的访问,并避免一些潜在的性能问题。

标签:Java,Thread,synchronized,ReentrantLock,线程,访问共享,Semaphore
From: https://www.cnblogs.com/xianfengzhike/p/17886962.html

相关文章

  • 已解决:若依更换日志EasyExcel框架导出报错 java.lang.NoClassDefFoundError: org/apac
    先描述一下当时的场景回忆看到出错了,我就想可能是哪个路径写错了,或者导依赖名字写对,或者说是多了少了标点符号什么的。然而,还是想简单了,检查重启后发现问题并没有解决。于是就把所有我改过的地方检查了一遍,检查和这个依赖相关的代码。发现还是没啥关系后来去找百度、百度给的......
  • kotlin协程和java线程有啥区别
    Kotlin协程相对于Java线程有一些优势,尤其在处理异步和并发任务时,提供了更加简洁、可读性更高的代码。以下是一些Kotlin协程相比于Java线程的优势:轻量级:协程是轻量级的,可以更高效地创建和销毁,不需要像线程那样消耗大量的系统资源。更好的可读性:使用协程可以避免......
  • 2023最新中级难度JavaScript面试题,包含答案。刷题必备!记录一下。
    好记性不如烂笔头内容来自面试宝典-中级难度JavaScript面试题合集问:如何实现在JavaScript中的操作settimeout/setinterval?在JavaScript中,setTimeout()和setInterval()是两个非常重要的函数,它们分别用于设置一次性延时执行的函数和周期性重复执行的函数。setTi......
  • 2023最新高级难度JavaScript面试题,包含答案。刷题必备!记录一下。
    好记性不如烂笔头内容来自面试宝典-高级难度JavaScript面试题合集问:请问你如何使用装饰器模式?装饰器模式是一种设计模式,它允许我们在不修改原有类的基础上,动态地添加新的功能或者行为。装饰器模式通过创建一个新的对象来包装原始对象,并提供与原始对象相同的方法接口,但是......
  • Java语言基础知识全总结
    一.Java的优点1.      跨平台性。一次编译,到处运行。Java编译器会将Java代码编译成能在JVM上直接运行的字节码文件,C++会将源代码编译成可执行的二进制代码文件,所以C++执行速度快2.      纯面向对象。Java所有的代码都必须在类中书写。C++兼具面向对象和面向过程的特......
  • Java-指令重排
    Java-指令重排指令重排(InstructionReordering)是指编译器或者处理器在不改变程序语义的前提下,重新安排指令的执行顺序,以优化性能或者满足硬件的执行特性。在多线程环境中,指令重排可能导致线程安全性问题,因为重排序可能改变原本按照程序顺序应该执行的操作次序。单线程-可提高程......
  • Java-引用类型
    Java-引用类型四种引用类型(强引用、软引用、弱引用、虚引用)在Java中具有不同的使用场景,可以根据程序的需求和内存管理的要求来选择适当的引用类型。1.强引用(StrongReference):使用场景:在绝大多数情况下,我们使用的都是强引用。当一个对象具有强引用时,垃圾回收器不会回收该对......
  • java JSON对象与字符串间的转换
    importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.JSONObject;//字符串转为JSON对象StringstrParam="{\"callerid\":\"013941128270\",\"timestart\":\"2021-07-2709:37:42\",\"status\"......
  • Java运算符
    1.运算符1.1算术运算符算术运算符是对数值型的变量进行运算publicclassOperator{publicstaticvoidmain(Stringargs[]){System.out.println(10/4);//2System.out.println(10.0/4);//2.5doubled=10/4;System.out.println(......
  • java 正则表达式 用法
    在一个复杂的字符串中,使用正则表达式来取其中某个值importjava.util.regex.*;//正则表达式引用//复杂的字符串Stringinput="{\"pbxToken\":\"1ja930jsdlij912h94hk5l35poeweer\"}"+"{\"LS_CallStatus_Event_Type\":\"\",\"callId......