Java中13 种锁的实现方式有哪些?

java中锁的详解 因此,重量级锁的开销较大,可能会导致线程阻塞和性能下降。但是,由于重量级锁提供了最高的线程安全性,所以在需要确保数据完整性和一致性的情况下,重量级锁是非常有用的。但是,由于轻量级锁不是互斥的,所以如果多个线程同时尝试获取锁,则可能导致数据不一致的问题。可重入锁(Reentrant Lock):可重入锁允许同一个线程多次获取同一个锁,这意味着线程可以在执行完同步代码后再次获取该锁而不需要重新竞争。自旋锁(Spin Lock):自旋锁是一种基于忙等待的同步机制,它在获取锁时会一直循环等待,直到获取到锁为止。 阅读详情

这篇文章会很详细的解说13中锁的实现方式,希望大家认真看哦~

1、悲观锁

正如其名,它是指对数据修改时持保守态度,认为其他人也会修改数据。因此在操作数据时,会把数据锁住,直到操作完成。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。如果加锁的时间过长,其他用户长时间无法访问,影响程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是长事务而言,这样的开销往往无法承受。

如果是单机系统,我们可以采用 JAVA 自带的 synchronized 关键字,通过添加到方法或同步块上,锁住资源 如果是分布式系统,我们可以借助数据库自身的锁机制来实现。

<pre class="prettyprint hljs sql" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">select * from 表名 where id= #{id} for update</pre>

使用悲观锁的时候,我们要注意锁的级别,MySQL innodb 在加锁时,只有明确的指定主键或(索引字段)才会使用 行锁​;否则,会执行 表锁,将整个表锁住,此时性能会很差。在使用悲观锁时,我们必须关闭 MySQL 数据库的自动提交属性,因为mysql默认使用自动提交模式。悲观锁适用于写多的场景,而且并发性能要求不高。

2、乐观锁

乐观锁,从字面意思也能猜到个大概,在操作数据时非常乐观,认为别人不会同时修改数据,因此乐观锁不会上锁 只是在 提交更新​ 时,才会正式对数据的冲突与否进行检测。如果发现冲突了,则返回错误信息,让用户决定如何去做,fail-fast 机制 。否则,执行本次操作。

分为三个阶段:数据读取、写入校验、数据写入。

如果是单机系统,我们可以基于JAVA 的 CAS来实现,CAS 是一种原子操作,借助硬件的比较并交换来实现。

如果是分布式系统,我们可以在数据库表中增加一个 版本号 字段,如:version。

<pre class="prettyprint hljs sql" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">update 表 
set ... , version = version +1 
where id= #{id} and version = #{version}</pre>

操作前,先读取记录的版本号,更新时,通过SQL语句比较版本号是否一致。如果一致,则更新数据。否则会再次读取版本,重试上面的操作。

3、分布式锁

JAVA 中的 synchronized​ 、ReentrantLock 等,都是解决单体应用单机部署的资源互斥问题。随着业务快速发展,当单体应用演化为分布式集群后,多线程、多进程分布在不同的机器上,原来的单机并发控制锁策略失效

此时我们需要引入 分布式锁,解决跨机器的互斥机制来控制共享资源的访问。

分布式锁需要具备哪些条件:

  • 与单机系统一样的资源互斥功能,这是锁的基础
  • 高性能获取、释放锁
  • 高可用
  • 具备可重入性
  • 有锁失效机制,防止死锁
  • 非阻塞,不管是否获得锁,要能快速返回

实现方式多种多样,基于 数据库、Redis​、以及 Zookeeper等,这里讲下主流的基于Redis的实现方式:

加锁:

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">SET key unique_value  [EX seconds] [PX milliseconds] [NX|XX]</pre>

通过原子命令,如果执行成功返回 1,则表示加锁成功。注意:unique_value 是客户端生成的唯一标识,区分来自不同客户端的锁操作 解锁要特别注意,先判断 unique_value 是不是加锁的客户端,是的话才允许解锁删除。毕竟我们不能删除其他客户端加的锁。

解锁:解锁有两个命令操作,需要借助 Lua 脚本来保证原子性。

<pre class="prettyprint hljs fsharp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">// 先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end</pre>

借助 Redis 的高性能,Redis 实现分布式锁也是目前主流实现方式。但任何事情有利有弊,如果加锁的服务器宕机了,当slave 节点还没来得及数据备份,那不是别的客户端也可以获得锁。

为了解决这个问题,Redis 官方设计了一个分布式锁 Redlock。

基本思路:让客户端与多个独立的 Redis 节点并行请求申请加锁,如果能在半数以上的节点成功地完成加锁操作,那么我们就认为,客户端成功地获得分布式锁,否则加锁失败。

4、可重入锁

可重入锁,也叫做递归锁,是指在同一个线程在调外层方法获取锁的时候,再进入内层方法会自动获取锁。

对象锁或类锁内部有计数器,一个线程每获得一次锁,计数器 +1;解锁时,计数器 -1。

有多少次加锁,就要对应多少次解锁,加锁与解锁成对出现。

Java 中的 ReentrantLock​ 和 synchronized 都是 可重入锁。可重入锁的一个好处是可一定程度避免死锁。

5、自旋锁

自旋锁是采用让当前线程不停地在循环体内执行,当循环的条件被其他线程改变时才能进入临界区。自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不断增加时,性能下降明显,因为每个线程都需要执行,会占用CPU时间片。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

自旋锁缺点:

  • 可能引发死锁。
  • 可能占用 CPU 的时间过长。

我们可以设置一个 循环时间​ 或 循环次数​,超出阈值时,让线程进入阻塞状态,防止线程长时间占用 CPU 资源。JUC 并发包中的 CAS 就是采用自旋锁,compareAndSet 是CAS操作的核心,底层利用Unsafe对象实现的。

<pre class="prettyprint hljs cpp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}</pre>

如果内存中 var1 对象的var2字段值等于预期的 var5,则将该位置更新为新值(var5 + var4),否则不进行任何操作,一直重试,直到操作成功为止。

CAS 包含了Compare和Swap 两个操作,如何保证原子性呢?CAS 是由 CPU 支持的原子操作,其原子性是在硬件层面进行控制。

特别注意,CAS 可能导致 ABA 问题,我们可以引入递增版本号来解决。

6、独享锁

独享锁,也有人叫它排他锁。无论读操作还是写操作,只能有一个线程获得锁,其他线程处于阻塞状态。

缺点:读操作并不会修改数据,而且大部分的系统都是 读多写少​,如果读读之间互斥,大大降低系统的性能。下面的 共享锁 会解决这个问题。

像Java中的 ReentrantLock​ 和 synchronized 都是独享锁。

7、共享锁

共享锁是指允许多个线程同时持有锁,一般用在读锁上。读锁的共享锁可保证并发读是非常高效的。读写,写读 ,写写的则是互斥的。独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

ReentrantReadWriteLock,其读锁是共享锁,其写锁是独享锁。

8、读锁/写锁

如果对某个资源是读操作,那多个线程之间并不会相互影响,可以通过添加读锁实现共享。如果有修改动作,为了保证数据的并发安全,此时只能有一个线程获得锁,我们称之为 写锁。读读是共享的;而 读写、写读 、写写 则是互斥的。

像 Java中的 ReentrantReadWriteLock 就是一种 读写锁。

9、公平锁/非公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,所有线程都在队列里排队,先来先获取的公平性原则。

优点:所有的线程都能得到资源,不会饿死在队列中。

缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,CPU 唤醒下一个阻塞线程有系统开销。

非公平锁:多个线程不按照申请锁的顺序去获得锁,而是同时以插队方式直接尝试获取锁,获取不到(插队失败),会进入队列等待(失败则乖乖排队),如果能获取到(插队成功),就直接获取到锁。

优点:可以减少 CPU 唤醒线程的开销,整体的吞吐效率会高点。

缺点:可能导致队列中排队的线程一直获取不到锁或者长时间获取不到锁,活活饿死。

Java 多线程并发操作,我们操作锁大多时候都是基于 Sync​ 本身去实现的,而 Sync 本身却是 ReentrantLock​ 的一个内部类,Sync 继承 AbstractQueuedSynchronizer。

像 ReentrantLock 默认是非公平锁,我们可以在构造函数中传入 true,来创建公平锁。

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}</pre>

10、可中断锁/不可中断锁

可中断锁:指一个线程因为没有获得锁在阻塞等待过程中,可以中断自己阻塞的状态。不可中断锁:恰恰相反,如果锁被其他线程获取后,当前线程只能阻塞等待。如果持有锁的线程一直不释放锁,那其他想获取锁的线程就会一直阻塞。

内置锁 synchronized 是不可中断锁,而 ReentrantLock 是可中断锁。

ReentrantLock获取锁定有三种方式:

  • lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于阻塞状态,直到该线程获取锁。
  • tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false。
  • tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false。
  • lockInterruptibly(),如果获取了锁定立即返回;如果没有获取锁,线程处于阻塞状态,直到获取锁或者线程被别的线程中断。

11、分段锁

分段锁其实是一种锁的设计,目的是细化锁的粒度,并不是具体的一种锁,对于ConcurrentHashMap 而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7 中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

当需要put元素的时候,并不是对整个HashMap加锁,而是先通过hashcode知道要放在哪一个分段中,然后对这个分段加锁,所以当多线程put时,只要不是放在同一个分段中,可支持并行插入。

12、锁升级(无锁|偏向锁|轻量级锁|重量级锁)

JDK 1.6之前,synchronized 还是一个重量级锁,效率比较低。但是在JDK 1.6后,JVM为了提高锁的获取与释放效率对 synchronized 进行了优化,引入了偏向锁和轻量级锁 ,从此以后锁的状态就有了四种:无锁、偏向锁、轻量级锁、重量级锁。这四种状态会随着竞争的情况逐渐升级,而且是不可降级。

无锁

无锁并不会对资源锁定,所有的线程都可以访问并修改同一个资源,但同时只有一个线程能修改成功。也就是我们常说的乐观锁。

偏向锁

偏向于第一个访问锁的线程,初次执行synchronized代码块时,通过 CAS 修改对象头里的锁标志位,锁对象变成偏向锁。

当一个线程访问同步代码块并获取锁时,会在 Mark Word​ 里存储锁偏向的线程 ID。在线程进入和退出同步块时不再通过 CAS 操作来加锁和解锁,而是检测 Mark Word 里是否存储着指向当前线程的偏向锁。轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令即可。

执行完同步代码块后,线程并不会主动释放偏向锁。当线程第二次再执行同步代码块时,线程会判断此时持有锁的线程是否就是自己(持有锁的线程ID也在对象头里),如果是则正常往下执行。由于之前没有释放锁,这里不需要重新加锁,偏向锁几乎没有额外开销,性能极高。

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。关于偏向锁的撤销,需要等待全局安全点,即在某个时间点上没有字节码正在执行时,它会先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁,恢复到无锁(标志位为01)或轻量级锁(标志位为00)的状态。

偏向锁是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗。

轻量级锁

当前锁是偏向锁,此时有多个线程同时来竞争锁,偏向锁就会升级为轻量级锁。轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过自旋方式来获取锁。

轻量级锁的获取有两种情况:

  • 当关闭偏向锁功能时。
  • 多个线程竞争偏向锁导致偏向锁升级为轻量级锁。一旦有第二个线程加入锁竞争,偏向锁就升级为轻量级锁(自旋锁)。

在轻量级锁状态下继续锁竞争,没有抢到锁的线程将自旋,不停地循环判断锁是否能够被成功获取。获取锁的操作,其实就是通过CAS修改对象头里的锁标志位。先比较当前锁标志位是否为“释放”,如果是则将其设置为“锁定”,此过程是原子性。如果抢到锁,然后线程将当前锁的持有者信息修改为自己。

重量级锁

如果线程的竞争很激励,线程的自旋超过了一定次数(默认循环10次,可以通过虚拟机参数更改),将轻量级锁升级为重量级锁(依然是 CAS 修改锁标志位,但不修改持有锁的线程ID),当后续线程尝试获取锁时,发现被占用的锁是重量级锁,则直接将自己挂起(而不是忙等),等待将来被唤醒。

重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。简言之,就是所有的控制权都交给了操作系统,由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资。

13、锁优化技术(锁粗化、锁消除)

锁粗化就是告诉我们任何事情都有个度,有些情况下我们反而希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。

举个例子:有个循环体,内部。

<pre class="prettyprint hljs matlab" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">for(int i=0;i<size;i++){
    synchronized(lock){
        ...业务处理,省略
    }
}</pre>

经过锁粗化的代码如下:

<pre class="prettyprint hljs gradle" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">synchronized(lock){
    for(int i=0;i<size;i++){
        ...业务处理,省略
    }
}</pre>

锁消除指的是在某些情况下,JVM 虚拟机如果检测不到某段代码被共享和竞争的可能性,就会将这段代码所属的同步锁消除掉,从而到底提高程序性能的目的。

锁消除的依据是逃逸分析的数据支持,如 StringBuffer​ 的 append()​ 方法,或 Vector​ 的 add() 方法,在很多情况下是可以进行锁消除的,比如以下这段代码:

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public String method() {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < 10; i++) {
        sb.append("i:" + i);
    }
    return sb.toString();
}</pre>

以上代码经过编译之后的字节码如下:

从上述结果可以看出,之前我们写的线程安全的加锁的 StringBuffer​ 对象,在生成字节码之后就被替换成了不加锁不安全的 StringBuilder​ 对象了,原因是 StringBuffer 的变量属于一个局部变量,并且不会从该方法中逃逸出去,所以我们可以使用锁消除(不加锁)来加速程序的运行。

Java中的13个原子操作类 Java并发包中的原子操作类提供了线程安全的变量更新方式。主要包括四类:原子更新基本类型(如AtomicInteger)、原子更新数组(如AtomicIntegerArray)、原子更新引用类型(如AtomicReference)和原子更新字段(如AtomicIntegerFieldUpdater)。这些类通过CAS机制实现无锁线程安全,提供getAndIncrement、compareAndSet等方法,适用于多线程环境下的变量操作。其中AtomicIntegerFieldUpdater需要配合volat 阅读详情

相关推荐

后端开发应彻底掌握的13 种锁的实现方式

最近有很多小伙伴给我留言,分布式系统时代,线程并发,资源抢占,"锁" 慢慢变得很重要。那么常见的锁都有哪些?

Linuxhus的博客 660

悲观锁的实现方式java_并发编程--锁--悲观锁和乐观锁

悲观锁和乐观锁并不是某个具体的“锁”而是一种并发编程的基本概念,是根据看待并发同步的角度。乐观锁和悲观锁最早出现在数据库的设计当中,后来逐渐被 Java 的并发包所引入。悲观锁悲观锁认为对于同一个数据的并发操作一定是会发生修改的,采取加锁的形式,悲观地认为,不加锁的并发操作一定会出问题。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中...

weixin_30920223的博客 5233

悲观锁和乐观锁的区别及实现方式

乐观锁适用于读多写少的场景,可以提高并发性能,但需要处理数据冲突的情况。乐观锁的核心思想是假设不会发生并发冲突,因此在访问共享资源时不加锁,而是通过版本号或时间戳等机制判断数据是否被其他线程修改。互斥锁是最常见的悲观锁实现方式,它在访问共享资源之前会对资源进行加锁,其他线程在获取不到该锁之前,会被阻塞。悲观锁和乐观锁是在并发编程中常用的两种锁机制,用于解决多线程环境下的数据一致性问题。悲观锁的核心思想是假设会发生并发冲突,因此在访问共享资源之前,会先加锁,确保其他线程无法同时访问该资源。

LogicGuruX的博客 274

Java线程并发中常见的锁

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。本文着重介绍了在java并发中常见的几种锁机制。 1.偏向锁   偏向锁是JDK1.6提出来的一种锁优化的机制。其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约...

weixin_30892889的博客 803

半桥LLC谐振变换器及同步整流MATLAB仿真(一)

LLC工作原理详细解析,工作过程详细拆解

qq_55203246的博客 3万+

jemalloc实战:5分钟搞定C程序内存泄漏检测(附PDF报告生成技巧)

本文详细介绍了使用jemalloc的heap profiling功能快速检测C/C++程序内存泄漏的实战方法。通过预编译库和LD_PRELOAD注入,无需修改代码即可启动低开销剖析,并利用jeprof工具进行深度分析和生成可视化PDF报告,帮助开发者高效定位线上或开发环境中的内存问题。

i3j4k5的博客 224

在 Spring 中,乐观锁和悲观锁的实现方式

Spring Data JPA 提供了 @Version 注解,可以方便地实现基于版本号的乐观锁。需要注意的是,乐观锁和悲观锁各有优缺点,在使用时需要根据具体业务需求来选择合适的锁机制。

Xmi2599的博客 2180

一、数学建模层次分析法(AHP)【清风数学建模个人笔记】

层次分析法: 层次分析法主要用于解决评价类问题(例如:选择哪种方案更好、哪位员工、运动员等表现得更优秀) 评价类问题: 分析问题得出评判问题的子标,分别给不同子标赋予权重(权重和要为1) ...

m0_58882109的博客 1万+

JAVA 中 13 种锁的实现方式

分布式系统时代,线程并发,资源抢占,慢慢变得很重要。那么常见的锁都有哪些?

qq_43842093的博客 2719

STM32F4实战:手把手教你实现USB OTG双模式切换(Host/Slave)

本文详细介绍了如何在STM32F4上实现USB OTG双模式切换(Host/Slave),包括硬件架构解析、软件实现机制、主机模式下的U盘读写以及从机模式下的自定义设备开发。通过实战案例和优化技巧,帮助开发者快速掌握USB双角色功能,提升嵌入式系统开发的灵活性和效率。

u9v0w的博客 728

MMRotate 详细使用教程

MMRotate 详细使用教程 做旋转目标检测,怎么可以不知道如何使用MMRotate呢! github链接: https://github.com/open-mmlab/mmrotate 1 安装 创建虚拟环境并激活 conda create -n mmrotate python=3.7 -y conda activate mmrotate 安装 PyTorch 和 torchvision conda install pytorch torchvision -c pytorch 注意:确保

Sylvia_Lan的博客 1万+

聊聊JAVA 中 13 种锁的实现方式

最近有很多小伙伴给我留言,分布式系统时代,线程并发,资源抢占,慢慢变得很重要。那么常见的锁都有哪些?

LoveSummer 2万+

保姆级教程:用AdGuard DNS代理实现全设备广告过滤(含Win/Mac/安卓/iOS配置)

本文提供了一份详尽的AdGuard DNS代理配置指南,帮助用户在全设备(Win/Mac/安卓/iOS)上实现广告过滤。通过系统级DNS设置,AdGuard能有效拦截广告、跟踪器和恶意网站,提升隐私保护和浏览效率。教程涵盖原理说明、各平台配置步骤及高级优化技巧,适合不同技术水平的用户。

weixin_42656416的博客 816

Java多线程实现方式及并发与同步,13-各种锁的理解

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2401_84411558的博客 852

广义S变换在MATLAB中的仿真

广义S变换是对信号进行时频分析的一种方法,它基于短时傅里叶变换(Short-Time Fourier Transform,STFT)和小波变换(Wavelet Transform)。广义S变换将信号分解成时频平面上的一系列矩形框,每个框内的数值表示相应时刻和频率下信号的能量。本文将介绍如何使用MATLAB进行广义S变换的仿真,并提供相应的源代码。函数,我们可以方便地计算信号的广义S变换,并获取时频平面上的时域和频域信息。广义S变换在信号处理和相关领域中具有广泛的应用,可以用于分析和处理各种类型的信号。

CodeNexus的博客 1119

RK3568平台(camera篇) mipi csi接口

MIPI联盟,即移动产业处理器接口(Mobile Industry Processor Interface 简称MIPI)联盟。MIPI(移动产业处理器接口)是MIPI联盟发起的为移动应用处理器制定的开放标准和一个规范。MIPI是差分串口传输,速度快,抗干扰。主流手机模组现在都是用MIPI传输,传输时使用4对差分信号传输图像数据和一对差分时钟信号;MIPI摄像头有三个电源:VDDIO(IO电源),AVDD(模拟电源),DVDD(内核数字电源),不同sensor模组的摄像头供电不同。

weixin_49303682的博客 5344

Java13的API_Java13新特性 -- 重新实现旧版套接字API

全新实现的 NioSocketImpl 来替换JDK1.0的PlainSocketImpl。它便于维护和调试,与 NewI/O (NIO) 使用相同的 JDK 内部结构,因此不需要使用系统本地代码。它与现有的缓冲区缓存机制集成在一起,这样就不需要为 I/O 使用线程栈。它使用 java.util.concurrent 锁,而不是 synchronized 同步方法,增强了并发能力。新的实现是Jav...

weixin_39703773的博客 125

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

聊聊 13 种锁的实现方式

如果线程的竞争很激励,线程的自旋超过了一定次数(默认循环10次,可以通过虚拟机参数更改),将轻量级锁升级为重量级锁(依然是 CAS 修改锁标志位,但不修改持有锁的线程ID),当后续线程尝试获取锁时,发现被占用的锁是重量级锁,则直接将自己挂起(而不是忙等),等待将来被唤醒。tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;...

公众号:微观技术 2022

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

这些13 种锁的实现方式你知道吗

最近有很多小伙伴给我留言,分布式系统时代,线程并发,资源抢占,"锁" 慢慢变得很重要。那么常见的锁都有哪些? 今天就和大家简单聊聊这个话题

Java_LingFeng的博客 1984

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Java进阶(锁)——锁分类总结,Java中常用的锁的介绍

Java进阶(锁)——锁分类总结,Java中常用的锁的介绍

Arya的博客,专注后端领域 6279

聊聊13 种锁的实现方式

分布式系统时代,线程并发,资源抢占,"锁" 慢慢变得很重要。那么常见的锁都有哪些?1、悲观锁正如其名,它是指对数据修改时持保守态度,认为其他人也会修改数据。因此在操作数据时,会把数据锁住,直到操作完成。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。如果加锁的时间过长,其他用户长时间无法访问,影响程序的并发访问性,同时这样对数据库性能开销影响也很大,特别是长事务而言,这样的开...

IT学习小镇 213
上一篇: Java加密算法有几种?
下一篇: Java——Spring事务属性、事务传播行为
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值