java提供了两种内置的锁的实现,一种是由JVM实现的synchronized和JDK提供的Lock,当你的应用是单机或者说单进程应用时,可以使用synchronized或Lock来实现锁。
synchronized:内部锁
https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
每个对象都绑定了一把锁,
并且一把锁同一时间只能被一个线程占有,
支持了happens-before
When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.
多线程同步序列
线程先获取锁,才能进入同步代码块,也就是先获取,再执行,
多个线程添加到同步序列,等待锁的释放,这个机制支持了多线程环境的
happens-before
Locks In Synchronized Methods:同步方法
Synchronized Statements:同步代码块
Reentrant Synchronization:可重入锁
Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by
another thread. But a thread can acquire a lock that it
already owns. Allowing a thread to acquire the same lock
more than once enables reentrant synchronization. This
describes a situation where synchronized code, directly or
indirectly, invokes a method that also contains
synchronized code, and both sets of code use the same
lock. Without reentrant synchronization, synchronized
code would have to take many additional precautions to
avoid having a thread cause itself to block.
当一个线程占有锁,
描述了一种情况,同一个线程链式调用多个同步代码块,或者多个同步方法;
死锁,饥饿,活锁(饥饿和活锁了解即可)
https://blog.csdn.net/zhousenshan/article/details/78777998
Lock锁
https://blog.csdn.net/ns_code/article/details/17487337
Java 5中引入了新的锁机制——java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作。Lock接口有3个实现它的类:ReentrantLock、ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入锁、读锁和写锁。lock必须被显式地创建、锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例化。为了保证锁最终一定会被释放(可能会有异常发生),要把互斥区放在try语句块内,并在finally语句块中释放锁,尤其当有return语句时,return语句必须放在try字句中,以确保unlock()不会过早发生,从而将数据暴露给第二个任务。因此,采用lock加锁和释放锁的一般形式如下:
Lock lock = new ReentrantLock();//默认使用非公平锁,如果要使用公平锁,需要传入参数true
........
lock.lock();
try {
//更新对象的状态
//捕获异常,必要时恢复到原来的不变约束
//如果有return语句,放在这里
} finally {
lock.unlock(); //锁必须在finally块中释放
}
ReetrankLock与synchronized比较:性能比较
在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。
相比之下使用Java提供的Lock对象,性能更高一些。Brian Goetz对这两种锁在JDK1.5、单核处理器及双Xeon处理器环境下做了一组吞吐量对比的实验,发现多线程环境下,synchronized的吞吐量下降的非常严重,而ReentrankLock则能基本保持在同一个比较稳定的水平上。
但与其说ReetrantLock性能好,倒不如说synchronized还有非常大的优化余地,于是到了JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地,所以还是提倡在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
ReetrankLock与synchronized比较:用途比较
1、等待可中断:
2、可实现公平锁:
3、锁可以绑定多个条件:
公平锁和非公平锁
可实现公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序排队等待,而非公平锁则不保证这点,在锁释放时,任何一个等待锁的线程都有机会获得锁。synchronized中的锁时非公平锁,ReentrantLock默认情况下也是非公平锁,但可以通过构造方法ReentrantLock(ture)来要求使用公平锁。
lock锁:获取锁的四种方式
https://segmentfault.com/q/1010000005602326
这个最好把Lock的四个锁法都比较一下(容我copy些东西):
void lock();
If the lock is not available then the current thread becomes disabled for thread scheduling purposes and lies dormant until the lock has been acquired.
在等待获取锁的过程中休眠并禁止一切线程调度
void lockInterruptibly() throws InterruptedException;
If the lock is not available then the current thread becomes disabled for thread scheduling purposes and lies dormant until one of two things happens:
The lock is acquired by the current thread; or Some other thread interrupts the current thread, and interruption of lock acquisition is supported.
在等待获取锁的过程中可被中断
boolean tryLock();
Acquires the lock if it is available and returns immediately with the value true. If the lock is not available then this method will return immediately with the value false.
获取到锁并返回true;获取不到并返回false
*boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
If the lock is available this method returns immediately with the value true. If the lock is not available then the current thread becomes disabled for thread scheduling purposes and lies dormant until one of three things happens:The lock is acquired by the current thread; or Some other thread interrupts the current thread, and interruption of lock acquisition is supported; or The specified waiting time elapses.
在指定时间内等待获取锁;过程中可被中断
假如线程A和线程B使用同一个锁LOCK,此时线程A首先获取到锁LOCK.lock(),并且始终持有不释放。如果此时B要去获取锁,有四种方式:
LOCK.lock(): 此方式会始终处于等待中,即使调用B.interrupt()也不能中断,除非线程A调用LOCK.unlock()释放锁。
LOCK.lockInterruptibly(): 此方式会等待,但当调用B.interrupt()会被中断等待,并抛出InterruptedException异常,否则会与lock()一样始终处于等待中,直到线程A释放锁。
LOCK.tryLock(): 该处不会等待,获取不到锁并直接返回false,去执行下面的逻辑。
LOCK.tryLock(10, TimeUnit.SECONDS):该处会在10秒时间内处于等待中,但当调用B.interrupt()会被中断等待,并抛出InterruptedException。10秒时间内如果线程A释放锁,会获取到锁并返回true,否则10秒过后会获取不到锁并返回false,去执行下面的逻辑。