java 常用锁
公平锁和非公平锁
1.公平锁,是指多个线程按照申请的顺序来获取锁,类似排队打饭,先来后到。
2.非公平锁,是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程
比先申请的线程优先获取锁,在高并发情况下,有可能会造成优先级反转或者饥饿现象。
Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。
可重入锁(也叫做递归锁)
指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程
在外层方法获取锁的时候,在进入内层方法会自动获取锁。也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
ReentrantLock/Synchronized就是一个典型的可重入锁,可重入锁的最大作用是避免死锁。
例子:
class Photo implements Runnable{
public synchronized void sendSMS() throws Exception {
System.out.println(Thread.currentThread().getName()+"\t invoked sendSMS()");
sendEmail();
}
public synchronized void sendEmail() throws Exception {
System.out.println(Thread.currentThread().getName()+"\t invoked sendEmail()");
}
@Override
public void run() {
get();
}
Lock lock = new ReentrantLock();
public void get() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t invoked sendSMS()");
set();
} finally {
lock.unlock();
}
}
public void set() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t invoked sendEmail()");
} finally {
lock.unlock();
}
}
}
public class ReentrantLockDemo {
public static void main(String[] args) {
Photo photo = new Photo();
new Thread(() ->{
try {
photo.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
new Thread(() ->{
try {
photo.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------------------");
Thread t3 = new Thread(photo,"t3");
Thread t4 = new Thread(photo,"t4");
t3.start();
t4.start();
}
}
运行结果见下图

自旋锁(spinlock)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
//unsafe.getAndAddInt(Object var1, long var2,int var4) {
int var 5;
do {
var5 = this.getIntVolatile(var1,var2);
} while(!this.compareAndSwapInt(var1,var2,var5,var5 + var4))
return var5;
}
例子:
public class SpinLockDemo {
//原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t come in");
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"\t come out");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() ->{
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnlock();
},"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() ->{
spinLockDemo.myLock();
spinLockDemo.myUnlock();
},"t2").start();
}
}
结果如下:

独占锁:指该锁一次只能被一个线程所持有,对ReentrantLock和Synchronized而言都是独占锁
共享锁:指该锁可被多个线程所持有,对于ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的
读写锁分离例子:
class MyReadWrite {
private volatile Map<String, Object> map = new HashMap<>();
//private Lock lock = new ReentrantLock();
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t正在写入:" + key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t写入完成:" + key);
} catch (Exception e) {
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t正在读取:");
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t读取完成:" + result);
} catch (Exception e) {
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyReadWrite myReadWrite = new MyReadWrite();
//模拟5个线程读写
for(int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() ->{
myReadWrite.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}
for(int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() ->{
myReadWrite.get(tempInt+"");
},String.valueOf(i)).start();
}
}
}
结果如下:

java 常用锁的更多相关文章
- Java常用锁机制简介
在开发Java多线程应用程序中,各个线程之间由于要共享资源,必须用到锁机制.Java提供了多种多线程锁机制的实现方式,常见的有synchronized.ReentrantLock.Semaphore. ...
- 工作常用4种Java线程锁的特点,性能比较、使用场景
多线程的缘由 在出现了进程之后,操作系统的性能得到了大大的提升.虽然进程的出现解决了操作系统的并发问题,但是人们仍然不满足,人们逐渐对实时性有了要求. 使用多线程的理由之一是和进程相比,它是一种非常花 ...
- JAVA常用知识点及面试题总结
1. String.StringBuffer.StringBuilder三者区别? (1)三者在执行速率上的比较: String<StringBuffer<StringBuilder 原因 ...
- Java 常用List集合使用场景分析
Java 常用List集合使用场景分析 过年前的最后一篇,本章通过介绍ArrayList,LinkedList,Vector,CopyOnWriteArrayList 底层实现原理和四个集合的区别.让 ...
- Java 各种锁的小结
一. synchronized 在 JDK 1.6 之前,synchronized 是重量级锁,效率低下. 从 JDK 1.6 开始,synchronized 做了很多优化,如偏向锁.轻量级锁.自旋锁 ...
- java常用英语单词
abstract (关键字) 抽象 ['.bstr.kt] access vt.访问,存取 ['.kses]'(n.入口,使用权) algorithm n.算法 ['.lg.riem] annotat ...
- JAVA中锁的解决方案
前言 在上一节中,我们给大家介绍了什么是锁,以及锁的使用场景,我相信大家对锁的定义,以及锁的重要性都有了比较清晰的认识.在这一节中,我们会给大家继续做深入的介绍,介绍JAVA为我们提供的不同种类的锁. ...
- Java常用英语汇总(面试必备)
Java常用英语汇总(面试必备) abstract (关键字) 抽象 ['.bstr.kt] access vt.访问,存 ...
- Java 常用类库与技巧【笔记】
Java 常用类库与技巧[笔记] Java异常体系 Java异常相关知识 Java在其创立的时候就设置了比较有效的处理机制,其异常处理机制主要回答了三个问题:what,where,why what表示 ...
随机推荐
- P1072 城市轰炸
题目描述 一个大小为N*M的城市遭到了X次轰炸,每次都炸了一个每条边都与边界平行的矩形. 在轰炸后,有Y个关键点,指挥官想知道,它们有没有受到过轰炸,如果有,被炸了几次,最后一次是第几轮. 输入格式 ...
- Webstorm 配置 Less编译
配置less编译
- 对input输入框日期显示格式化
先:导入fmt标签 在对input框进行属性设置 <input type="text" name="createtime" value="&l ...
- jquery银行电子账单表格填入和编辑插件
jquery银行电子账单表格填入和编辑 前段时间做的一个银行表格账单的jquery插件,用于金额写入和编辑的应用,希望对大家有所帮助,发现问题欢迎回复 ;(function($){ function ...
- Linux 内核 MCA 总线
微通道体系(MCA)是一个 IBM 标准, 用在 PS/2 计算机和一些笔记本电脑. 在硬件级别, 微通道比 ISA 有更多特性. 它支持多主 DMA, 32-位地址和数据线, 共享中断线, 和地理 ...
- dotnet 数组自动转基类数组提示 Co-variant array conversion 是什么问题
在 C# 的语法,可以提供自动将某个类的数组自动转这个类的基类数组的方法,但是这样的转换在 Resharper 会提示 Co-variant array conversion 这是什么问题? 在 C# ...
- linux zookeeper开机启动
1.在zkEnv.sh中指定当前用户jdk环境变量 export JAVA_HOME=/usr/local/src/jdk1.7.0_55/ 2.在/etc/rc.d/init.d文件夹下创建zook ...
- eclipse中如何配置maven
1.首先需要在自己电脑中安装Maven,下载maven的路径:http://maven.apache.org/download.cgi 2.我们把下载好的文件解压到自己电脑的任意一个盘符中去,我的是e ...
- APP数据采集--基础配置
一.首先需要下载fiddler,可以直接在官网下载最新版呢 二.设置允许抓取HTTPS数据: 打开已经下载好了的fiddler,打开在工具栏中的Tools,之后在HTTPS的工具栏下面进行如下的设置 ...
- 基于ambassador实现K8S灰度发布
为什么需要灰度发布 灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式.在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对 ...