网站首页 > 精选教程 正文
1.可重入锁
可重入的意思是在一个线程中可以多次获取同一把锁而不会出现死锁,ReentrantLock和synchronized都是可重入锁。
public class ReentrantTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
System.out.println("第1次获取锁:" + this);
int index = 1;
while (true) {
synchronized (this) {
System.out.println("第" + (++index) + "次获取锁:" + this);
}
if (index == 5) {
break;
}
}
}
}
}).start();
}
}
从执行结果可以看出,多次获取的都是同一把锁。
2.公平锁/非公平锁
- 公平锁是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁;
- 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁;非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高;缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁;
public class FairLockTest {
//公平锁/非公平锁
ReentrantLock reentrantLock = new ReentrantLock(true);
public static void main(String[] args) {
FairLockTest fairLockTest = new FairLockTest();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 进入队列");
//调用加公平锁/非公平锁的方法尝试获取锁
fairLockTest.test();
}
}).start();
}
}
public void test(){
reentrantLock.lock();
System.out.println("获得锁:" + Thread.currentThread().getName());
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
从结果可以看出,使用公平锁时进入队列的顺序和获取锁的顺序是一致的。
将公平锁修改为非公平锁
ReentrantLock reentrantLock = new ReentrantLock(false);
多次执行的结果都不同,有可能后申请的线程比先申请的线程优先获取锁
3.共享锁/排他锁
共享锁是指该锁可被多个线程所持有,排他锁是指该锁一次只能被一个线程持有。
共享锁:ReadWriteLock的读锁。排他锁:Synchronized、ReentrantLock、ReadWriteLock的写锁。
public class ReentrantReadWriteLockTest {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void read() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取读锁");
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放读锁");
}
}
public static void write() {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取写锁");
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + "释放写锁");
}
}
public static void main(String[] args) {
new Thread(() -> write(), "Thread-0").start();
new Thread(() -> read(), "Thread-1").start();
new Thread(() -> read(), "Thread-2").start();
new Thread(() -> write(), "Thread-3").start();
new Thread(() -> write(), "Thread-4").start();
}
}
线程1和线程2可以同时获取读锁但必须等线程0的写锁释放,线程3必须等线程1和线程2的读锁释放后才能获取写锁,线程4必须等线程3释放写锁以后才能获取写锁。
以此得出结论:读锁是共享锁,写锁是排他锁,读锁和写锁不能同时存在
4.乐观锁/悲观锁
- 悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,对于同一个数据的并发操作,悲观锁采取加锁的形式;
- 乐观锁认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重试的方式更新数据
悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
Java 中的 Synchronized 和 ReentrantLock 等独占锁(排他锁)就是一种悲观锁思想的实现,因为 Synchronzied 和 ReetrantLock 不管是否持有资源,它都会尝试去加锁。
乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过。具体的实现方案一般来说有两种: 版本号机制 和 CAS(java.util.concurrent.atomic包下的原子类就是使用CAS实现)
5.偏向锁/轻量级锁/重量级锁
- 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价
- 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能
- 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低
一个对象刚开始实例化的时候,没有任何线程来访问它。它是可偏向的,意味着,它现在认为只可能有一个线程来访问它,所以当第一个线程来访问它的时候,它会偏向这个线程,此时,对象持有偏向锁。偏向第一个线程,这个线程在修改对象头成为偏向锁的时候使用CAS操作,并将对象头中的ThreadID改成自己的ID,之后再次访问这个对象时,只需要对比ID,不需要再使用CAS在进行操作。
一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象是偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁。如果不存在使用了,则可以将对象恢复成无锁状态,然后重新偏向。
轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转。
- 上一篇: Java中的锁是什么意思,有哪些分类?
- 下一篇: Java 锁的那些事儿
猜你喜欢
- 2024-11-21 Java中的重重“锁”事
- 2024-11-21 线程进阶:多任务处理——Java中的锁(Unsafe基础)
- 2024-11-21 深入理解MySQL锁机制原理
- 2024-11-21 Java并发锁的原理,你所不知道的Java“锁”事
- 2024-11-21 阿里二面:你知道Java中的同步与锁机制详解?
- 2024-11-21 知识点深度解读系列-JAVA锁
- 2024-11-21 图解Java中的锁:什么是死锁?怎么排查死锁?怎么避免死锁?
- 2024-11-21 Java锁与线程的那些“不可描述”的事儿
- 2024-11-21 让人闻风丧胆的 Mysql 锁机制
- 2024-11-21 Java中各种锁的理解
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)