文章目录
- 1.重入锁Reentrantlock
- 总结:synchronized关键字与可重入锁的区别
- 2.sychronized的锁优化
- 锁偏向
- 轻量级锁
- 自旋锁
- 锁消除
- 3.ConcurrentHashmap分段锁
1.重入锁Reentrantlock
简单举例:
import java.util.concurrent.locks.ReentrantLock;
public class ReenterLock implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public static int i = 0;
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
lock.lock();
try {
i++;
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock tl = new ReenterLock();
Thread t1 = new Thread(tl);
Thread t2 = new Thread(tl);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
重入锁是指当前线程可以反复进入同一个锁。
如:
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
lock.lock();
lock.lock();
try {
i++;
} finally {
lock.unlock();
lock.unlock();
}
}
}
与synchronized相比,重入锁可进行中断响应
如下:
import java.util.concurrent.locks.ReentrantLock;
public class IntLock implements Runnable {
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
public IntLock(int lock) {
this.lock = lock;
}
@Override
public void run() {
try {
if (lock == 1) {
lock1.lockInterruptibly();//可以对中断进行响应的锁请求
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
lock2.lockInterruptibly();
System.out.println("线程完成!");
} else {
lock2.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread())
lock1.unlock();
if (lock2.isHeldByCurrentThread())
lock2.unlock();
System.out.println(Thread.currentThread().getId() + ":线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
IntLock intLock1 = new IntLock(1);
IntLock intLock2 = new IntLock(2);
Thread t1 = new Thread(intLock1);
Thread t2 = new Thread(intLock2);
t1.start();
t2.start();
Thread.sleep(1000);
t2.interrupt();
}
}
在代码最后,由于线程t2中断,所以t2会放弃对lock1的申请,同时放弃已经获得的lock2。该操作会导致t1可以顺利得到lock2并执行下去。因此,最后的结果会打印一个线程完成,两个线程退出。
并且可以进行锁申请等待限时:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TimeLock implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
Thread.sleep(6000);
System.out.println(Thread.currentThread().getId()+":Job Done!");
} else {
System.out.println(Thread.currentThread().getId()+":get lock failed");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) {
TimeLock timeLock = new TimeLock();
Thread t1 = new Thread(timeLock);
Thread t2 = new Thread(timeLock);
t1.start();
t2.start();
}
}
由于t1等待6s,导致t2在5s之内并没有获得锁,所以t2输出get lock failed,而t1输出job done.
重入锁还可以进行公平性设定,意思是当线程1和线程2都在申请锁,非公平锁是随机选择线程获得锁,而公平锁是先到先得。但是由于公平锁需要维护一个有序队列,因此其性能较低,在默认情况下是非公平的。
总结:synchronized关键字与可重入锁的区别
1.可重入性:
二者均可重入
2.锁的实现:
synchronized依赖于JVM实现,而ReentrantLock基于JDK实现,通过阅读源码可以了解实现,区别就类似于操作系统控制实现与用户使用代码实现。
3.性能区别:
在synchronized优化以前,性能比ReentrantLock差很多,但自从synchronize引入了偏向锁、轻量级锁(自选锁)后 ,也就是自循锁后,两者性能差不多(JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”)。
4.功能区别:
- 便利性:synchronized更便利,它是由编译器保证加锁与释放。ReentrantLock是需要手动声明与释放锁,所以为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
- 锁的细粒度和灵活度:ReentrantLock优于synchronized
5.ReentrantLock独有的功能:
- ReentrantLock可以指定是公平锁还是非公平锁,synchronized只能是非公平锁。(所谓公平锁就是先等待的线程先获得锁)
- 提供了一个Condition类,可以分组唤醒需要唤醒的线程。不像是synchronized要么随机唤醒一个线程,要么全部唤醒。
- 提供能够中断等待锁的线程的机制
2.sychronized的锁优化
锁偏向
核心思想:如果一个线程获得了锁,那么锁就进入偏向模式。当这个线程再次请求锁时,无须再做任何同步操作。
轻量级锁
核心思想:将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁。如果线程获得轻量级锁成果,则可以进入到临界区,否则则会膨胀为重量级锁。
自旋锁
核心思想:锁膨胀后,为避免线程挂起,虚拟机会让当前线程做几个空循环,在经过若干次循环后,若能获得锁,则进入临界区,否则挂起。
锁消除
核心思想:Java虚拟机再JIT编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁。
3.ConcurrentHashmap分段锁