自旋锁:线程状态及上下文切换消耗系统资源,当访问共享资源的时间短,频繁上下文切换不值得。jvm实现,使线程在没有获得锁的时候,不被挂起,转而执行空循环,循环几次之后,如果还没能获得锁,则被挂起。
阻塞锁:阻塞锁改变了线程的运行状态,让线程进入阻塞状态进行等待,当获得相应的信号(唤醒或者时间)时,才可以进入线程的准备就绪状态,转为就绪状态的所有线程,通过竞争,进入运行状态。
重入锁:支持线程再次进入的锁,就像我们有房间的钥匙,可以多次进入房间类似。
读写锁:两把锁,读锁跟写锁,写写互斥、读写互斥、读读共享。
互斥锁:当线程抢占到资源,其他线程进不来。
悲观锁: 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
公平锁:所有线程老老实实排队,对大家而言都很公平。
非公平锁:一部分线程排着队,但是新来的线程可能插队。
偏向锁:偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。
独占锁:独占锁模式下,每次只能有一个线程能持有锁。
共享锁:允许多个线程同时获取锁,并发访问共享资源。
(1)lock于synchronized的区别
(2)Lock继承体系图
(3)Lock常用的API
public class LockTest {
private Lock lock = new ReentrantLock();
/**
* 当前线程释放锁后,其他线程才可以获取到锁
*/
public void lock(){
lock.lock();
try {
System.out.println("线程"+Thread.currentThread().getName()+":获取到锁资源");
Thread.sleep(2000L);
}catch (Exception e){
System.out.println("线程"+Thread.currentThread().getName()+":释放锁发生异常");
}finally {
lock.unlock();
System.out.println("线程"+Thread.currentThread().getName()+":释放锁完毕");
}
}
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(()->{
lockTest.lock();
}).start();
new Thread(()->{
lockTest.lock();
}).start();
}
}
public class LockTest {
private Lock lock = new ReentrantLock();
/**
* 线程尝试获取线程锁,如果有其他线程占用,就返回false,无法拿到锁
*/
public void lock(){
if(lock.tryLock()){
try {
System.out.println("线程"+Thread.currentThread().getName()+":获取到锁资源");
Thread.sleep(2000L);
}catch (Exception e){
System.out.println("线程"+Thread.currentThread().getName()+":释放锁发生异常");
}finally {
lock.unlock();
System.out.println("线程"+Thread.currentThread().getName()+":释放锁完毕");
}
}else{
System.out.println("线程"+Thread.currentThread().getName()+":尝试获取锁失败,其他线程持有锁");
}
}
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(()->{
lockTest.lock();
}).start();
new Thread(()->{
lockTest.lock();
}).start();
}
}
public class LockTest {
private Lock lock = new ReentrantLock();
/**
* 线程尝试获取线程锁,如果有其他线程占用,就返回false,无法拿到锁
*/
public void lock() throws InterruptedException {
if(lock.tryLock(3000, TimeUnit.MILLISECONDS)){
try {
System.out.println("线程"+Thread.currentThread().getName()+":获取到锁资源");
Thread.sleep(5000L);
}catch (Exception e){
System.out.println("线程"+Thread.currentThread().getName()+":释放锁发生异常");
}finally {
lock.unlock();
System.out.println("线程"+Thread.currentThread().getName()+":释放锁完毕");
}
}else{
System.out.println("线程"+Thread.currentThread().getName()+":尝试获取锁失败,其他线程持有锁");
}
}
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(()->{
try {
lockTest.lock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
lockTest.lock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
(1)可重入锁简介
(2)伪代码案例
class A
public synchronized methodA(){
methodB();
}
public synchronized methodB(){
}
#当线程调用A类的对象methodA同步方法,如果其他线程没有获取A类的对象锁,那么当前线程就获得点钱A类对象的锁,然后执行methodB同步方法,当前线程能够在次获取A类对象的锁,其他线程是不可以的,这就是可重入锁。
(3)自定义锁发生死锁问题
public class MyLock extends ReentrantLock {
//定义锁的标志位
private boolean isHoldLock = false;
/**
* 同一时刻,能且仅能有一个线程获取到锁
* 其他线程只能等待该线程释放锁之后才能获取到锁
*/
@Override
public synchronized void lock() {
//判断是否有当前线程持有锁,如果有就进入等待
if (isHoldLock) {
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
//将锁的标志置成true,表示当前线程持有锁
isHoldLock = true;
}
@Override
public synchronized void unlock() {
//唤醒等待的线程
notify();
//释放锁资源
isHoldLock = false;
}
}
public class ReentryDemo {
private Lock lock = new MyLock();
public void methodA(){
lock.lock();
System.out.println("进入方法A");
methodB();
lock.unlock();
}
public void methodB(){
lock.lock();
System.out.println("进入方法B");
lock.unlock();
}
public static void main(String[] args) {
ReentryDemo reentryDemo = new ReentryDemo();
reentryDemo.methodA();
}
}
(4)自定义可重入锁
public class MyLock extends ReentrantLock {
private boolean isHoldLock = false;
//当前线程实例
private Thread holdLockThread = null;
//重入的次数
private int reentryCount = 0;
/**
* 同一时刻,能且仅能有一个线程获取到锁
* 其他线程只能等待该线程释放锁之后才能获取到锁
*/
@Override
public synchronized void lock() {
//判断当前线程是否持有锁,并且是不是同一个线程重复进入
if(isHoldLock && Thread.currentThread() != holdLockThread){
try{
wait();
}catch (Exception e){
e.printStackTrace();
}
}
holdLockThread = Thread.currentThread();
isHoldLock = true;
//记录加锁的次数
reentryCount++;
}
@Override
public synchronized void unlock() {
//判断当前线程是否是持有锁的线程,是,重入次数减1,不是就不做处理
if(Thread.currentThread() == holdLockThread){
reentryCount--;
if(reentryCount == 0){
notify();
isHoldLock = false;
}
}
}
}
测试代码和上面相同,此处省略
执行结果:
(5) 一个线程执行同步代码时,再次重入该锁过程中,如果抛出异常,会释放锁吗?
public class ReentryDemo {
private Lock lock = new MyLock();
private int num = 0;
public void methodA() throws InterruptedException {
lock.lock();
System.out.println("进入方法A");
methodB();
Thread.sleep(3000L);
if (num == 0) {
num++;
int a = 1 / 0;
}
lock.unlock();
}
public void methodB() {
lock.lock();
System.out.println("进入方法B");
lock.unlock();
}
public static void main(String[] args) {
ReentryDemo reentryDemo = new ReentryDemo();
new Thread(() -> {
try {
reentryDemo.methodA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
reentryDemo.methodA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public class ReentryDemo {
private Lock lock = new MyLock();
private int num = 0;
public void methodA() {
try {
lock.lock();
System.out.println("进入方法A");
methodB();
Thread.sleep(3000L);
if (num == 0) {
num++;
int a = 1 / 0;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void methodB() {
try {
lock.lock();
System.out.println("进入方法B");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentryDemo reentryDemo = new ReentryDemo();
new Thread(() -> {
reentryDemo.methodA();
}).start();
new Thread(() -> {
reentryDemo.methodA();
}).start();
}
}