Java线程锁(一)

简介: 在Linux系统下,启动一个新的进程必须要给它分配独立的地址空间,建立众多的数据表来维护它的代码段,堆栈段和数据段,这是一种昂贵的多任务工作方式。而在进程中同时运行多个线程,多个线程彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于一个进程所花费的空间,而且线程之间彼此切换所需要的时间远远小于进程之间切换所需要的时间

多线程并发面临的挑战:


多线程资源共享

线程死锁

锁的选择

由于多个线程是共同占用所属进程的资源和地址空间的,如果多个线程要同时访问某个资源怎么办?


其实在Java并发编程中,经常遇到多个线程访问同一个 共享资源,这时候作为开发者必须考虑如何维护数据一致性,这就是Java锁机制(同步问题)的来源


Java提供了多种多线程锁机制的实现方式,常见的有:


Synchronized

ReentrantLock

Semaphore

Atomiclnteger等

每种机制都有优缺点与各自的适用场景


Synchronized(依赖于JVM,可靠性高,性能随Java版本升级而提高)

在Java中Synchronized关键字被常用于维护数据一致性


Synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是按顺序的


Synchronized来实现多线程的同步操作是很简单的,在需要同步的对方的代码块,类,方法中加入此关键字,它能保证在同一个时刻最多只有一个线程执行同一个对象的同步代码,可保证修饰的代码在执行过程中不会被其他线程干扰。


当线程使用Synchronized等待锁时 是不能被Thread.interrupt () 中断的,否则会造成线程死锁


Synchronized修饰的代码具有原子性和可见性,在需要进程同步的程序中使用的频率非常高,可满足一般的进程同步要求


ReentrantLock(可重入锁)

ReentrantLock(可重入锁)可以被线程多次重复进行获取操作


ReentrantLock继承接口Lock并实现了接口中定义的方法,除了能完成Synchronized所能完成的所有工作,还提供了可响应中断锁,可轮询锁请求,定时锁等 避免多线程死锁的方法


Lock依赖于特殊的CPU,可以认为不受JVM的约束,可以通过其他语言平台来完成底层的实现


Synchronized与ReentrantLock的性能比


在并发量较小的多线程应用程序中,ReentrantLock与Synchronized性能相差无几


在高并发量的情况下,Synchronized性能迅速下降几十倍,而ReetrantLock的性能仍然维持一个水准


建议:在高并发量的情况下使用ReentrantLock


ReentrantLock引入了两个概念:   公平锁与非公平锁


公平锁指的是锁的分配机制是公平的,通常 先对锁提出获取请求的线程 会先被分配到锁。


非公平锁指的是JVM按随机,就近原则分配锁的机制被称为非公平锁


ReentrantLock在构造函数中提供了是否公平锁的初始化,默认是非公平锁


非公平锁实际执行的效率远超公平锁,除非程序有特殊要求,否则常用非公平锁的分配机制


ReentrantLock通过 lock ()  与 unlock () 来进行加锁与解锁操作,与Synchronized会被JVM自动解锁机制不同,ReentrantLock加锁后需要手动进行解锁


为了避免程序出现异常而无法正常解锁的情况,使用ReentrantLock必须在finally控制块中进行解锁操作


使用方式的示例代码如下:


Lock lock=new ReentrantLock();

   try{

       lock.lock();

   }catch(){

   }finally{

       lock.unlock();

   }

Semaphore

由于Synchronized和ReentrantLock是互斥锁,互斥是进程同步关系的一种特殊情况,相当于只存在一个临界资源,因此最多只能给一个线程提供服务。在实际复杂的多线程应用程序中可能存在多个临界资源,这时可以借助Semaphore信号量来完成多个临界资源的访问


Semaphore基本能完成ReentrantLock的所有工作,使用方法类似,通过acquire () 与 release () 方法来获取和释放临界资源


Semaphore.accquire () 方法默认为可响应中断锁,与ReentrantLock.lockInterruptibly () 作用效果一致,也就是说在等待临界资源的过程中可以被Thread.interrupt () 方法中断


Semaphore也实现了 可轮询的锁请求 与 定时锁 的功能,除了方法名accquire与lock不同,使用方法与ReentrantLock几乎一致。Semaphore也提供了公平锁与非公平锁机制,也可以在构造函数中进行初始化设定


Semaphore的锁释放操作也由手动进行,与ReentrantLock一样,为避免线程因抛出异常而无法正常释放锁的情况发生,释放锁的操作也必须在finally代码块中完成


AtomicInteger

AtomicInterger是一系列相同类的代表之一,常见的还有AtomicLong等,它们的实现原理都相同,区别就在于运算对象类型的不同


在多线程应用程序中,如++i  ,i++等运算不具有原子性,是不安全的线程操作之一


通常使用Synchronized将该操作变成一个原子操作,但JVM为此类操作特意提供了一些同步类,从而使得更加方便,并且程序运行效率变得更高,


AtomicInteger的性能是ReentrantLock的好几倍


Java线程锁总结:


Synchronized:


在资源竞争不是很激烈的情况下,偶尔有同步的情况下,Synchronized是很合适的,因为编译程序通常会尽可能的优化Synchronized,可读性也很好


ReentrantLock:


在资源竞争不激烈的情况下,性能稍微比Synchronized差一点,但是当同步很激烈情况下,Synchronized的性能一下子能下降好几十倍,而ReentrantLock依然能维持常态


在高并发量的情况下使用ReentrantLock


Atomic:


在资源竞争不激烈的情况下,性能比Synchronized差一点,而资源竞争激烈的情况下,也能维持常态


在资源竞争激烈的情况下,Atomic的性能会优于ReentrantLock一倍左右


缺点在于,只能同步一个值,一段代码中只能出现一个Atomic的变量,多余一个同步无效,它不能在多个Atomic之间同步


个人建议:


写同步的时候,优先考虑Synchronized,如果有特殊的情况,再进一步优化


ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难


目录
相关文章
|
2天前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
23 10
线程安全问题和锁
|
14天前
|
存储 Java
Java锁是什么?简单了解
在高并发环境下,锁是Java中至关重要的概念。锁或互斥是一种同步机制,用于限制多线程环境下的资源访问,确保排他性和并发控制。例如,超市储物柜仅能存放一个物品,若三人同时使用,则需通过锁机制确保每次只有一个线程访问。Java中可以通过`synchronized`关键字实现加锁,确保关键代码段的原子性,避免数据不一致问题。正确使用锁可有效提升程序的稳定性和安全性。
Java锁是什么?简单了解
|
14天前
|
小程序 Java 开发工具
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
本文通过一个生动的例子,探讨了Java中加锁仍可能出现超卖问题的原因及解决方案。作者“JavaDog程序狗”通过模拟空调租赁场景,详细解析了超卖现象及其背后的多线程并发问题。文章介绍了四种解决超卖的方法:乐观锁、悲观锁、分布式锁以及代码级锁,并重点讨论了ReentrantLock的使用。此外,还分析了事务套锁失效的原因及解决办法,强调了事务边界的重要性。
42 2
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
|
16天前
|
数据采集 存储 安全
如何确保Python Queue的线程和进程安全性:使用锁的技巧
本文探讨了在Python爬虫技术中使用锁来保障Queue(队列)的线程和进程安全性。通过分析`queue.Queue`及`multiprocessing.Queue`的基本线程与进程安全特性,文章指出在特定场景下使用锁的重要性。文中还提供了一个综合示例,该示例利用亿牛云爬虫代理服务、多线程技术和锁机制,实现了高效且安全的网页数据采集流程。示例涵盖了代理IP、User-Agent和Cookie的设置,以及如何使用BeautifulSoup解析HTML内容并将其保存为文档。通过这种方式,不仅提高了数据采集效率,还有效避免了并发环境下的数据竞争问题。
如何确保Python Queue的线程和进程安全性:使用锁的技巧
|
6天前
|
Oracle Java 关系型数据库
【颠覆性升级】JDK 22:超级构造器与区域锁,重塑Java编程的两大基石!
【9月更文挑战第6天】JDK 22的发布标志着Java编程语言在性能和灵活性方面迈出了重要的一步。超级构造器和区域锁这两大基石的引入,不仅简化了代码设计,提高了开发效率,还优化了垃圾收集器的性能,降低了应用延迟。这些改进不仅展示了Oracle在Java生态系统中的持续改进和创新精神,也为广大Java开发者提供了更多的可能性和便利。我们有理由相信,在未来的Java编程中,这些新特性将发挥越来越重要的作用,推动Java技术不断向前发展。
|
16天前
|
Java 开发者
Java多线程教程:使用ReentrantLock实现高级锁功能
Java多线程教程:使用ReentrantLock实现高级锁功能
19 1
|
18天前
|
安全 Java 开发者
【锁的艺术】StampedLock:Java并发编程的新武器!
【8月更文挑战第24天】`StampedLock`, 作为 Java 8 引入的新特性,为开发者提供了一种相较于传统 `ReentrantReadWriteLock` 更高效且灵活的锁机制。它属于 `java.util.concurrent.locks` 包,主要特点包括乐观读锁,这在多读少写的场景下能显著提升性能。
28 1
|
22天前
|
存储 安全 容器
【多线程面试题二十一】、 分段锁是怎么实现的?
这篇文章解释了分段锁的概念和实现方式,通过将数据分成多个段并在每段数据上使用独立锁,从而降低锁竞争,提高并发访问效率,举例说明了`ConcurrentHashMap`如何使用分段锁技术来实现高并发和线程安全。
【多线程面试题二十一】、 分段锁是怎么实现的?
|
22天前
|
安全 Java
【多线程面试题十九】、 公平锁与非公平锁是怎么实现的?
这篇文章解释了Java中`ReentrantLock`的公平锁和非公平锁的实现原理,其中公平锁通过检查等待队列严格按顺序获取锁,而非公平锁允许新线程有更高机会立即获取锁,两者都依赖于`AbstractQueuedSynchronizer`(AQS)和`volatile`关键字以及CAS技术来确保线程安全和锁的正确同步。
【多线程面试题十九】、 公平锁与非公平锁是怎么实现的?
|
9天前
|
安全 Java API
Java线程池原理与锁机制分析
综上所述,Java线程池和锁机制是并发编程中极其重要的两个部分。线程池主要用于管理线程的生命周期和执行并发任务,而锁机制则用于保障线程安全和防止数据的并发错误。它们深入地结合在一起,成为Java高效并发编程实践中的关键要素。
8 0