synchronized的锁是针对多线程的,从线程的角度去思考才能真正弄明白.
Java的synchronized使用方法总结
①.. 把synchronized当作函数修饰符时
同步块,示例代码如下:
{
synchronized(so)
//.....
}
这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码.当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁.
Class Foo
public synchronized static void methodAAA() // 同步的static 函数
//....
public void methodBBB()
synchronized(Foo.class) // class literal(类名称字面常量)
代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了).
【1】公平所和非公平所.
公平锁:是指按照申请锁的顺序来获取锁,
非公平所:线程获取锁的顺序不一定按照申请锁的顺序来的.
//默认是不公平锁,传入true为公平锁,否则为非公平锁
ReentrantLock reentrantLock = new ReetrantLock();
独享锁:一次只能被一个线程所访问
共享锁:线程可以被多个线程所持有.
ReadWriteLock 读锁是共享锁,写锁是独享锁.
乐观锁:对于一个数据的操作并发,是不会发生修改的.在更新数据的时候,会尝试采用更新,不断重入的方式,更新数据.
悲观锁:对于同一个数据的并发操作,是一定会发生修改的.所以呢对于同一个数据的并发操作,悲观锁采用加锁的形式.悲观锁认为,不加锁的操作一定会出问题,
轻量级锁
重量级锁
自旋锁
自旋锁(Spin lock)
自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是
否该自旋锁的保持者已经释放了锁,"自旋"一词就是所以呢而得名.其作用是为了解决某项资源的互斥使用.因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远
高于互斥锁.虽然它的效率比互斥锁高,但是它也有些不足之处:
①.、自旋锁一直占用CPU,他在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时 间内获得锁,这无疑会使CPU效率降低.
所以呢我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作.自旋锁适用于锁使用者保持锁时间比较短的情况下.
两种锁的加锁原理
互斥锁:线程会从sleep(加锁)——running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销.
自旋锁:线程一直是running(加锁——解锁),死循环检测锁的标志位,机制不复杂.
互斥锁属于sleep-waiting类型的锁.例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和
Core1上.假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞
(blocking),Core0 会在此时进行上下文切换(Context
Switch)将线程A置于等待队列中,此时Core0就可以运行其他的任务(例如另一个线程C)而不必进行忙等待.而自旋锁则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在
Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止.
两种锁的区别
互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长.
两种锁的应用
互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑
①. 临界区有IO操作
至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器.
lock与Syntronized的区别
转自自:
java并发之Lock与synchronized的区别
①.)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized.所以说,在具体使用时要根据适当情况选择.
两者在锁的相关概念上区别:
①可重入锁
看下面这段代码就明白了:
class MyClass
public synchronized void method1()
而由于synchronized和Lock都具备可重入性,所以不会发生上述现象.
可中断锁:顾名思义,就是可以相应中断的锁.
在Java中,synchronized就不是可中断锁,而Lock是可中断锁.
如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁.
在前面演示lockInterruptibly()的用法时已经体现了Lock的可中断性.
公平锁即尽量以请求锁的顺序来获取锁.比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁.
非公平锁即无法保证锁的获取是按照请求锁的顺序进行的.这样就可能导致某个或者一些线程永远获取不到锁.
在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序.
而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁.
我们可以在创建ReentrantLock对象时,通过以下方式来设置锁的公平性:
ReentrantLock
lock = new ReentrantLock(true);
如果参数为true表示为公平锁,为fasle为非公平锁.默认情况下,如果使用无参构造器,则是非公平锁.
另外在ReentrantLock类中定义了很多方法,比如:
isFair() //判断锁是否是公平锁
isLocked() //判断锁是否被任何线程获取了
isHeldByCurrentThread() //判断锁是否被当前线程获取了
hasQueuedThreads() //判断是否有线程在等待该锁
在ReentrantReadWriteLock中也有类似的方法,同样也可以设置为公平锁和非公平锁.不过要记住,ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口.
正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突.
ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口.
可以通过readLock()获取读锁,通过writeLock()获取写锁.
性能比较
synchronized(这里的对象你看成一道门) {
这里是一个人进来了,把门反锁了
需要同步操作的代码
这里是里面的人事情做完了,出去了,门开着其他人可以进了
至于怎么锁的,这是java和jvm的规定和实现细节,作为普通程序员没必要深入那么多.
读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只读操作,一个用于写入操作.只要没有writer,读取锁可以由多个reader线程同时保持.写入锁是独占的.
互斥锁一次只允许一个线程访问共享数据,哪怕进行的是只读操作;读写锁允许对共享数据进行更高级别的并发访问:对于写操作,一次只有一个线程(write线程)可以修改共享数据,对于读操作,允许任意数量的线程同时进行读取.
与互斥锁相比,使用读写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数.
读写锁适用于读多写少的情况.
可重入读写锁 ReentrantReadWriteLock
属性ReentrantReadWriteLock 也是基于 AbstractQueuedSynchronizer 实现的,它具有下面这些属性(来自Java doc文档):
* 获取顺序:此类不会将读取者优先或写入者优先强加给锁访问的排序.
* 非公平模式(默认):连续竞争的非公平锁可能无限期地推迟一个或多个reader或writer线程,但吞吐量通常要高于公平锁.
* 公平模式:线程利用一个近似到达顺序的策略来争夺进入.当释放当前保持的锁时,可以为等待时间最长的单个writer线程分配写入锁,如果有一组等待时间大于所有正在等待的writer线程的reader,将为该组分配读者锁.
* 试图获得公平写入锁的非重入的线程将会阻塞,除非读取锁和写入锁都自由(这意味着没有等待线程).
* 重入:此锁允许reader和writer按照 ReentrantLock 的样式重新获取读取锁或写入锁.在写入线程保持的所有写入锁都已经释放后,才允许重入reader使用读取锁.
writer可以获取读取锁,但reader不能获取写入锁.
* 锁降级:重入还允许从写入锁降级为读取锁,实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁.但是,从读取锁升级到写入锁是不可能的.
* 锁获取的中断:读取锁和写入锁都支持锁获取期间的中断.
* Condition 支持:写入锁提供了一个 Condition 实现,对于写入锁来说,该实现的行为与 ReentrantLock.newCondition() 提供的Condition 实现对 ReentrantLock 所做的行为相同.当然,此 Condition 只能用于写入锁.
读取锁不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException.
* 监测:此类支持一些确定是读取锁还是写入锁的方法.这些方法设计用于监视系统状态,而不是同步控制.
对于 ReentrantLock,它是可重入的独占锁,内部的 Sync 类实现了 tryAcquire(int)、tryRelease(int) 方法,并用状态的值来表示重入次数,加锁或重入锁时状态加 1,释放锁时状态减 1,状态值等于 0 表示锁空闲.
对于 CountDownLatch,它是一个关卡,在条件满足前阻塞所有等待线程,条件满足后允许所有线程通过.内部类 Sync 把状态初始化为大于 0 的某个值,当状态大于 0 时所有wait线程阻塞,每调用一次 countDown 方法就把状态值减 1,减为 0 时允许所有线程通过.利用了AQS的共享模式.
现在,要用AQS来实现 ReentrantReadWriteLock.
一点思考问题
* AQS只有一个状态,那么如何表示 多个读锁 与 单个写锁 呢?
* ReentrantLock 里,状态值表示重入计数,现在如何在AQS里表示每个读锁、写锁的重入次数呢?
* 如何实现读锁、写锁的公平性呢?
第一段:synchronized和lock的用法区别
synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象.
lock:需要显示指定起始位置和终止位置.一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效.且在加锁和解锁处需要通过lock()和unlock()显示指出.所以一般会在finally块中写unlock()以防死锁.
用法区别比较简单,这里不赘述了,如果不懂的可以看看Java基本语法.
第二段:synchronized和lock性能区别
而Lock用的是乐观锁方式.所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止.乐观锁实现的机制就是CAS操作(Compare and Swap).我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState.这里其实就是调用的CPU提供的特殊指令.
现代的CPU提供了指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定.这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法.
我也只是了解到这一步,具体到CPU的算法如果感兴趣的读者还可以在查阅下,如果有更好的解释也可以给我留言,我也学习下.
第三段:synchronized和lock用途区别
①某个线程在等待一个锁的控制权的这段时间需要中断
以上就是土嘎嘎小编为大家整理的java自旋门锁代码相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!