java中的锁(Java中有哪些锁,区别是什么)
本文目录
- Java中有哪些锁,区别是什么
- 说说java锁有哪些种类,以及区别
- java线程锁有几种
- Java锁有哪些种类,以及区别
- java中悲观锁和乐观锁的区别
- java中的锁有哪几种
- java一些能降低竞争锁的方法
- Java如何实现对Mysql数据库的行锁(java代码实现数据库锁)
Java中有哪些锁,区别是什么
【1】公平所和非公平所。 公平锁:是指按照申请锁的顺序来获取锁, 非公平所:线程获取锁的顺序不一定按照申请锁的顺序来的。//默认是不公平锁,传入true为公平锁,否则为非公平锁ReentrantLock reentrantLock = new ReetrantLock();12【2】共享锁和独享锁 独享锁:一次只能被一个线程所访问 共享锁:线程可以被多个线程所持有。 ReadWriteLock 读锁是共享锁,写锁是独享锁。 【3】乐观锁和悲观锁。 乐观锁:对于一个数据的操作并发,是不会发生修改的。在更新数据的时候,会尝试采用更新,不断重入的方式,更新数据。 悲观锁:对于同一个数据的并发操作,是一定会发生修改的。因此对于同一个数据的并发操作,悲观锁采用加锁的形式。悲观锁认为,不加锁的操作一定会出问题, 【4】分段锁 1.7及之前的concurrenthashmap。并发操作就是分段锁,其思想就是让锁的粒度变小。 【5】偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价 轻量级锁 重量级锁 【6】自旋锁 自旋锁
说说java锁有哪些种类,以及区别
锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及。本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑。1、自旋锁自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。如下 01 public class SpinLock { 02 03 private AtomicReference《Thread》 sign =newAtomicReference《》(); 04 05 public void lock(){ 06 Thread current = Thread.currentThread(); 07 while(!sign .compareAndSet(null, current)){ 08 } 09 } 10 11 public void unlock (){ 12 Thread current = Thread.currentThread(); 13 sign .compareAndSet(current, null); 14 } 15 } 使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。注:该例子为非公平锁,获得锁的先后顺序,不会按照进入lock的先后顺序进行。Java锁的种类以及辨析(二):自旋锁的其他种类锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及。本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑。 2.自旋锁的其他种类上篇我们讲到了自旋锁,在自旋锁中 另有三种常见的锁形式:TicketLock ,CLHlock 和MCSlockTicket锁主要解决的是访问顺序的问题,主要的问题是在多核cpu上01 package com.alipay.titan.dcc.dal.entity; 02 03 import java.util.concurrent.atomic.AtomicInteger; 04 05 public class TicketLock { 06 private AtomicInteger serviceNum = new AtomicInteger(); 07 private AtomicInteger ticketNum = new AtomicInteger(); 08 private static final ThreadLocal《Integer》 LOCAL = new ThreadLocal《Integer》(); 09 10 public void lock() { 11 int myticket = ticketNum.getAndIncrement(); 12 LOCAL.set(myticket); 13 while (myticket != serviceNum.get()) { 14 } 15 16 } 17 18 public void unlock() { 19 int myticket = LOCAL.get(); 20 serviceNum.compareAndSet(myticket, myticket + 1); 21 } 22 } 每次都要查询一个serviceNum 服务号,影响性能(必须要到主内存读取,并阻止其他cpu修改)。CLHLock 和MCSLock 则是两种类型相似的公平锁,采用链表的形式进行排序,01 importjava.util.concurrent.atomic.AtomicReferenceFieldUpdater; 02 03 public class CLHLock { 04 public static class CLHNode { 05 private volatile boolean isLocked = true; 06 } 07 08 @SuppressWarnings("unused") 09 private volatileCLHNode tail; 10 private static finalThreadLocal《CLHNode》 LOCAL = new ThreadLocal《CLHNode》(); 11 private static finalAtomicReferenceFieldUpdater《CLHLock, CLHNode》 UPDATER = AtomicReferenceFieldUpdater.newUpdater(CLHLock.class, 12 CLHNode.class,"tail"); 13 14 public void lock() { 15 CLHNode node = new CLHNode(); 16 LOCAL.set(node); 17 CLHNode preNode = UPDATER.getAndSet(this, node); 18 if (preNode != null) { 19 while (preNode.isLocked) { 20 } 21 preNode = null; 22 LOCAL.set(node); 23 } 24 } 25 26 public void unlock() { 27 CLHNode node = LOCAL.get(); 28 if (!UPDATER.compareAndSet(this, node,null)) { 29 node.isLocked = false; 30 } 31 node = null; 32 } 33 } CLHlock是不停的查询前驱变量, 导致不适合在NUMA 架构下使用(在这种结构下,每个线程分布在不同的物理内存区域)MCSLock则是对本地变量的节点进行循环。不存在CLHlock 的问题。01 importjava.util.concurrent.atomic.AtomicReferenceFieldUpdater; 02 03 public class MCSLock { 04 public static class MCSNode { 05 volatile MCSNode next; 06 volatile boolean isLocked = true; 07 } 08 09 private static finalThreadLocal《MCSNode》 NODE = new ThreadLocal《MCSNode》(); 10 @SuppressWarnings("unused") 11 private volatileMCSNode queue; 12 private static finalAtomicReferenceFieldUpdater《MCSLock, MCSNode》 UPDATER = AtomicReferenceFieldUpdater.newUpdater(MCSLock.class, 13 MCSNode.class,"queue"); 14 15 public void lock() { 16 MCSNode currentNode = new MCSNode(); 17 NODE.set(currentNode); 18 MCSNode preNode = UPDATER.getAndSet(this, currentNode); 19 if (preNode != null) { 20 preNode.next = currentNode; 21 while (currentNode.isLocked) { 22 23 } 24 } 25 } 26 27 public void unlock() { 28 MCSNode currentNode = NODE.get(); 29 if (currentNode.next == null) { 30 if (UPDATER.compareAndSet(this, currentNode, null)) { 31 32 } else { 33 while (currentNode.next == null) { 34 } 35 } 36 } else { 37 currentNode.next.isLocked = false; 38 currentNode.next = null; 39 } 40 } 41 }
java线程锁有几种
1、自旋锁2、自旋锁的其他种类3、阻塞锁4、可重入锁5、读写锁6、互斥锁7、悲观锁8、乐观锁9、公平锁10、非公平锁11、偏向锁12、对象锁13、线程锁14、锁粗化15、轻量级锁16、锁消除17、锁膨胀18、信号量
Java锁有哪些种类,以及区别
一、公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。
二、可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。
三、独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,当然是独享锁。
四、互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock
五、乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
六、分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
七、偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
八、自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
典型的自旋锁实现的例子,可以参考自旋锁的实现
java中悲观锁和乐观锁的区别
乐观锁和悲观锁的区别如下:1、悲观锁是当线程拿到资源时,就对资源上锁,并在提交后,才释放锁资源,其他线程才能使用资源。2、乐观锁是当线程拿到资源时,上乐观锁,在提交之前,其他的锁也可以操作这个资源,当有冲突的时候,并发机制会保留前一个提交,打回后一个提交,让后一个线程重新获取资源后,再操作,然后提交。和git上传代码一样,两个线程都不是直接获取资源本身,而是先获取资源的两个copy版本,然后在这两个copy版本上修改。3、悲观锁和乐观锁在并发量低的时候,性能差不多,但是在并发量高的时候,乐观锁的性能远远优于悲观锁。4、常用的synchronized是悲观锁,lock是乐观锁。
java中的锁有哪几种
lock比synchronized比较如下:1) 支持公平锁,某些场景下需要获得锁的时间与申请锁的时间相一致,但是synchronized做不到 2) 支持中断处理,就是说那些持有锁的线程一直不释放,正在等待的线程可以放弃等待。如果不支持中断处理,那么线程可能一直无限制的等待下去,就算那些正在占用资源的线程死锁了,正在等待的那些资源还是会继续等待,但是ReentrantLock可以选择放弃等待 3) condition和lock配合使用,以获得最大的性能JAVA中锁使用的几点建议:1.如果没有特殊的需求,建议使用synchronized,因为操作简单,便捷,不需要额外进行锁的释放。鉴于JDK1.8中的ConcurrentHashMap也使用了CAS+synchronized的方式替换了老版本中使用分段锁(ReentrantLock)的方式,可以得知,JVM中对synchronized的性能做了比较好的优化。2.如果代码中有特殊的需求,建议使用Lock。例如并发量比较高,且有些操作比较耗时,则可以使用支持中断的所获取方式;如果对于锁的获取,讲究先来后到的顺序则可以使用公平锁;另外对于多个变量的锁保护可以通过lock中提供的condition对象来和lock配合使用,获取最大的性能。
java一些能降低竞争锁的方法
本文介绍一下提升并发可伸缩性的一些方式:减少锁的持有时间,降低锁的粒度,锁分段、避免热点域以及采用非独占的锁或非阻塞锁来代替独占锁。
减少锁的持有时间
降低发生竞争可能性的一种有效方式就是尽可能缩短锁的持有时间。例如,可以将一些与锁无关的代码移出同步代码块,尤其是那些开销较大的操作,以及可能被阻塞的操作,例如I/O操作。
降低锁的粒度
另一种减小锁的持有时间的方式是降低线程请求锁的频率(从而减小发生竞争的可能性)。这可以通过锁分解和锁分段等技术来实现,在这些技术中将采用多个相互独立的锁来保护独立的状态变量,从而改变这些变量在之前由单个锁来保护的情况。这些技术能减小锁操作的粒度,并能实现更高的可伸缩性,然而,使用的锁越多,那么发生死锁的风险也就越高。
锁分段
在某些情况下,可以将锁分解技术进一步扩展为对一组独立对象上的锁进行分解,这种情况被称为锁分段。例如,在ConcurrentHashMap的实现中使用了一个包含16个锁的数组,每个锁保护所有散列桶的1/16,其中第N个散列桶由第(Nmod16)个锁来保护。假设散列函数具有合理的分布性,并且关键字能够实现均匀分布,那么这大约能把对于锁的请求减少到原来的1/16。正是这项技术使得ConcurrentHashMap能够支持多达16个并发的写入器。(要使得拥有大量处理器的系统在高访问量的情况下实现更高的并发性,还可以进一步增加锁的数量,但仅当你能证明并发写入线程的竞争足够激烈并需要突破这个限制时,才能将锁分段的数量超过默认的16个。)
避免热点域
如果一个锁保护两个独立变量X和Y,并且线程A想要访问X,而线程B想要访问Y(这类似于在ServerStatus中,一个线程调用addUser,而另一个线程调用addQuery),那么这两个线程不会在任何数据上发生竞争,即使它们会在同一个锁上发生竞争。当每个操作都请求多个变量时,锁的粒度将很难降低。这是在性能与可伸缩性之间相互制衡的另一个方面,一些常见的优化措施,例如将一些反复计算的结果缓存起来,都会引入一些“热点域(HotField)”,而这些热点域往往会限制可伸缩性。当实现HashMap时,你需要考虑如何在size方法中计算Map中的元素数量。最简单的方法就是,在每次调用时都统计一次元素的数量。一种常见的优化措施是,在插入和移除元素时更新一个计数器,虽然这在put和remove等方法中略微增加了一些开销,以确保计数器是最新的值,但这将把size方法的开销从O(n)降低到O(l)。
代替独占锁
第三种降低竞争锁的影响的技术就是放弃使用独占锁,从而有助于使用一种友好并发的方式来管理共享状态。例如,使用并发容器、读-写锁、不可变对象以及原子变量。霍营北大青鸟发现ReadWriteLock能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变性可以完全不需要加锁操作。
Java如何实现对Mysql数据库的行锁(java代码实现数据库锁)
下面通过一个例子来说明
场景如下:
用户账户有余额,当发生交易时,需要实时更新余额。这里如果发生并发问题,那么会造成用户余额和实际交易的不一致,这对公司和客户来说都是很危险的。
那么如何避免:
网上查了下,有以下两种方法:
1、使用悲观锁
当需要变更余额时,通过代码在事务中对当前需要更新的记录设置forupdate行锁,然后开始正常的查询和更新操作
这样,其他的事务只能等待该事务完成后方可操作
当然要特别注意,如果使用了Spring的事务注解,需要配置一下:
在指定代码处添加事务注解
@
@Override
publicboolean(LonguserId,BigDecimalamount)
throws{
longtime=System.();
//获取对记录的锁定
UserBalancebalance=.getLock(userId);
LOGGER.info("start.time:{}",time);
if(null==balance){
thrownew(
.ERRORCODE_BALANCE_NOTEXIST,"userbalanceisnotexist");
}
booleanresult=.(balance,amount);
longtimeEnd=System.();
LOGGER.info("end.time:{}",timeEnd);
returnresult;
}
MyBatis中的锁定方式,实际测试该方法确实可以有效控制,不过在大并发量的情况下,可能会有性能问题吧
select*fromuser_balancewhereid=#{id,jdbcType=BIGINT}forupdate;
]]《
2、使用乐观锁
这个方法也同样可以解决场景中描述的问题(我认为比较适合并不频繁的操作):
设计表的时候增加一个version(版本控制字段),每次需要更新余额的时候,先获取对象,update的时候根据version和id为条件去更新,如果更新回来的数量为0,说明version已经变更
需要重复一次更新操作,如下:sql脚本
updateuser_balancesetBalance=#{balance,jdbcType=DECIMAL},Version=Version1whereId=#{id,jdbcType=BIGINT}andVersion=#{version,jdbcType=BIGINT}
这是一种不使用数据库锁的方法,解决方式也很巧妙。当然,在大量并发的情况下,一次扣款需要重复多次的操作才能成功,还是有不足之处的。不知道还有没有更好的方法。
更多文章:
小牛vp(永久免费)加速器下载(在百度下载的小牛加速器是官网吗)
2024年6月25日 01:23
你心目中的好医生应该是什么样的?人民的健康有保障,医生要有医德,这句话对吗
2024年5月16日 14:35
中关村在线手机排行榜(2010全球十大智能手机排行,诺基亚排第几哪款手机)
2024年6月26日 21:04
sd卡不能删除和格式化(sd卡只能读取文件,不能删除,不能格式化)
2024年9月6日 08:45
超级转换秀 破解(求超级转换秀的破解版,要是需要注册的就免了感激不尽)
2024年5月10日 10:05
安卓备份软件(安卓什么软件可以自动云端备份手机上的指定文件夹)
2024年7月25日 17:45