Java面试——锁
公平锁:是指多个线程按照申请锁的顺序来获取锁,有点先来后到的意思。在并发环境中,每个线程在获取锁时会先查看此锁维护的队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照 FIFO 的规则从队列中取到自己。
非公平锁:指多个线程获取锁的顺序并不是按照申请锁的顺序,上来就尝试占有锁,如果尝试失败,就再采用类似公平锁的方式获取锁。有可能后申请的线程比先申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象。
ReentrantLock:并发包中 ReentrantLock 的创建可以指定构造函数的 boolean 类型来得到公平锁或非公平锁,默认是false(非公平锁)。非公平的优点在于吞吐量比公平锁大。对于Synchronized 锁也是一种非公平锁。
可重入锁(又名递归锁):指同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码。也就是说,线程可以进入任何一个它已经拥有的锁,所同步的代码块。synchronized 和 unlock 都是可重入锁。
//简单理解,就是方法1 是一个同步方法,里面包含了一个方法2 也是同步方法,但是当进入方法1后,也就获得了方法2的锁,即可重入锁
public synchronized void method1(){
System.out.println("方法1 synchronized");
method2();
}
public synchronized void method2(){
System.out.printf("方法2 synchronized");
}
自旋锁:是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,这样的好处是减少了上下文切换的消耗,确定是循环会消耗 CPU。循环比较直到成功为止。
public final int getAndAddInt(Object var1, long var2, int var4){
int var5;
do{
//根据对象和地址偏移量获取内存中的值
var5 = this.getIntVolatile(var1, var2);
//将获取到的值 var5 传入,此方法内部会先比较var2地址的值是否等于 var5,相等则修改var5值并返回,否则重新进入循环。
}while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
手写一个自旋锁:思想就是通过 while 中的循环条件来充当锁,当条件成立时,表示未获得锁,进行死循环,直到 while 条件不成立,也就是获得锁。就退出死循环,执行业务逻辑。具体查看如下代码:
public class Test {
public static void main(String[] args) throws Exception {
/*执行结果展示: AA myLock
BB myLock
AA unLock
BB unLock
* 分析:我们 AA 线程休眠了 5秒足以让 BB 线程执行结束,那为什么 BB 执行到 myLock 之后就没有继续执行呢。
* 其实,BB 一直执行着,只不过陷入了 while 死循环中,因为 AA 将线程置为非空。
* 等到 5 秒后,AA unlock 重新将线程=null时,BB 获取线程并执行任务。over
*/
Test test = new Test();
new Thread(()->{
test.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.unLock();
},"AA").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
test.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.unLock();
},"BB").start();
}
//对线程保证原子性
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//获取锁,其实质,将锁看做一个条件判断,只要这个判断能够保证线程安全即可。
//如下:我们将线程是否为空作为条件,如果是空的就没锁,自己可以对其加锁,将其值设为自己。
//如果使用完,使用unlock 将线程设置为 null,其他线程通过判断来获得锁,其实就像一种约定而已。
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+" myLock");
while (!atomicReference.compareAndSet(null,thread)){
}
}
//释放锁
public void unLock(){
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(thread.getName()+" unLock");
}
}
【独占锁】(写锁):指该锁只能被一个线程所持有。对 ReentrantLock 和 Synchronized 而言都是独占锁。
【共享锁】(读锁):指该锁可被多个线程持有。
【1】不加读写锁时,代码及出现的问题如下:创建5个线程进行写入,5个线程进行读取。
public class ReadWriteLock {
private volatile Map map = new HashMap();
//写入方法
public void put(String k,Object v){
System.out.println(Thread.currentThread().getName()+" 开始写入:"+k );
try {
TimeUnit.MICROSECONDS.sleep(30);
}catch (Exception e){
e.printStackTrace();
}
map.put(k,v);
System.out.println(Thread.currentThread().getName()+" 写入完成");
}
//读方法
public void get(String k){
System.out.println(Thread.currentThread().getName()+" 读数据开始:"+k );
try {
TimeUnit.MICROSECONDS.sleep(10);
}catch (Exception e){
e.printStackTrace();
}
Object v = map.get(k);
System.out.println(Thread.currentThread().getName()+" 读数据完场完成"+v);
}
public static void main(String[] args) {
ReadWriteLock readWriteLock = new ReadWriteLock();
//写入数据
for (int i=1;i<6;i++){
final int tempInt = i;
new Thread(()->{
readWriteLock.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}
//读取数据
for(int i=1;i<6;i++){
final int tempInt = i;
new Thread(()->{
readWriteLock.get(tempInt+"");
},String.valueOf(i)).start();
}
}
}
【2】上述代码输出如下:第一个线程未写入完成时,其他线程就进入了该方法,进行了写操作。不符合多线程安全特性。

【3】加入读写锁:ReentrantReadWriteLock(读写锁)位于 JUC 包下
public class ReadWriteLock{
private volatile Map map = new HashMap();
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
//写入方法
public void put(String k,Object v){
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" 开始写入:"+k );
try {
TimeUnit.MICROSECONDS.sleep(30);
}catch (Exception e){
e.printStackTrace();
}
map.put(k,v);
System.out.println(Thread.currentThread().getName()+" 写入完成");
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.writeLock().unlock();
}
}
//读方法
public void get(String k){
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" 读数据开始:"+k );
try {
TimeUnit.MICROSECONDS.sleep(10);
}catch (Exception e){
e.printStackTrace();
}
Object v = map.get(k);
System.out.println(Thread.currentThread().getName()+" 读数据完场完成"+v);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
public static void main(String[] args) {
ReadWriteLock readWriteLock = new ReadWriteLock();
//写入数据
for (int i=1;i<6;i++){
final int tempInt = i;
new Thread(()->{
readWriteLock.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}
//读取数据
for(int i=1;i<6;i++){
final int tempInt = i;
new Thread(()->{
readWriteLock.get(tempInt+"");
},String.valueOf(i)).start();
}
}
}
【4】加入读写锁后,输出如下:


----关注公众号,获取更多内容----
Java面试——锁的更多相关文章
- 转:最近5年133个Java面试问题列表
最近5年133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...
- java面试宝典(蓝桥学院)
Java面试宝典(蓝桥学院) 回答技巧 这套面试题主要目的是帮助那些还没有java软件开发实际工作经验,而正在努力寻找java软件开发工作的学生在笔试/面试时更好地赢得好的结果.由于这套试题涉及的范围 ...
- JAVA面试精选【Java基础第一部分】
这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对于那些正打算找工作JAVA软件开发工作的童 ...
- java面试和笔试大全 分类: 面试 2015-07-10 22:07 10人阅读 评论(0) 收藏
2.String是最基本的数据类型吗? 基本数据类型包括byte.int.char.long.float.double.boolean和short. java.lang.String类是final类型 ...
- 近5年133个Java面试问题列表
Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来越高级,面试官问的问题也更深入. 在我 ...
- java 面试
115个Java面试题和答案——终极列表(上) 本文我们将要讨论Java面试中的各种不同类型的面试题,它们可以让雇主测试应聘者的Java和通用的面向对象编程的能力.下面的章节分为上下两篇,第一 ...
- Java面试题精选(三) JSP/Servlet Java面试逻辑题
-- JSP/Servlet Java面试逻辑题 -- 很显然,Servlet/JSP的WEB前端动态制作的重要性比HTML/CSS/JS的价值高很多,但我们都知道他们都是建立在HT ...
- JAVA面试精选
JAVA面试精选[Java基础第一部分] 这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对 ...
- Java面试系列
如果你的面试简历是如下这样写的,请务必准备回答下面的所有问题. 面试职位:Java高级工程师 专业技能: (1)牢固掌握Java基础知识,如集合.并发.I/O等,并对Java源码有一定的研究. (2) ...
- java面试笔试大汇总
java面试笔试题大汇总5 JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象:2.继承:3.封装:4. 多态性: 2.String是最基本的数据类型吗? 基本数据类型包括byte.int. ...
随机推荐
- PHP Redis - 事务
Redis 事务可以一次执行多个命令, 并有两个重要的保证: ① 事务是一个单独的隔离操作:事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中,不会被其他客户端发送来的命令请求所打断. ② ...
- cuda安装的问题
小学期老师给的文档,里面要加入这几个环境变量 他这排版有问题,我就去网上找了几个cuda环境变量的配置 保姆级的CUDA的下载安装使用,详细的环境变量配置,不仅仅让你能够安装,还会教你弄懂为什么要这样 ...
- 更多Linux实用命令
更多实用命令 进程相关 当程序运行在系统上时,我们称之为进程(process).想监测这些进程,需要熟悉 ps/top 等命令的用法.ps 命令好比工具中的瑞士军刀,它能输出运行在系统上的所有程序的许 ...
- Java 实现汉字按照首字母分组排序
一.实现思路: 1.将数据list 进行排序Collections,排序后是按照汉字字母排序的 2.循环找出26个字母,以字母为key,以list中相同首字母的数据为值(集合) 二.代码实现: // ...
- 项目:表格打印(字符图网格进阶、rjust、列表中最长的字符串长度)
项目要求:编写一个名为 printTable()的函数,它接受字符串的列表的列表,将它显示在组织良好的表格中,每列右对齐. tableData = [['apples', 'oranges', 'ch ...
- 使用python的turtle库画一个冰墩墩
目录 先看效果图 设置一个画布 画左手和手内 画轮廓和其他部分 画细节(眼睛.鼻子.嘴巴等) 画头部彩虹 画五环标志 最后(别忘记还有一个结束) 先看效果图 设置一个画布 点击查看代码 import ...
- cmake 实现交叉编译注意事项
(1)确保安装交叉编译工具安装成功 在终端输入arm-linux-gnueabihf-g++ -v 或 arm-linux-gnueabihf-gcc -v ,能看到相应交叉C编译器和C++编译器的版 ...
- PHP实现JWT登录鉴权
一.什么是JWT 1.简介 JWT(JSON Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准. 简单的说,JWT就是一种Token的编码算法,服务器端负责根据一个 ...
- IO在指定目录建文件
path= "d:\\Project\\log\\log.txt"; File file = new File(path); //创建文件目录 if(!file.getParent ...
- 划分数据集时出现PermissionError: [Errno 13] Permission denied:
PermissionError: [Errno 13] Permission denied: [errno 13]权限被拒绝 错误的原因可能是文件找不到,或者被占用,或者无权限访问,或者打开的不是文件 ...