Java基础——锁
1、锁
当一个共享资源被多方访问时为了避免发生冲突而施加的一种机制
2、乐观锁和悲观锁
Java中锁在宏观分为乐观锁和悲观锁
乐观锁:是一种乐观思想,认为多读少写,一般情况下数据在修改时不会出现冲突,所以在数据访问之前不会加锁,只是在数据提交更改时,才会对数据进行检测
适用场景:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量
乐观锁实现:大部分都是通过 CAS(Compare And Swap,比较并交换)操作实现的,CAS 是一个多线程同步的原子指令,CAS 操作包含三个重要的信息,即内存位置、预期原值和新值。如果内存位置的值和预期的原值相等的话,那么就可以把该位置的值更新为新值,否则不做任何修改,Java从5.0开始引入了对CAS的支持,与之对应的是java.util.concurrent.atomic 包下的AtomicInteger、AtomicReference等类,它们提供了基于CAS的读写操作和并发环境下的内存可见性
悲观锁:是一种是悲观思想,认为写多,遇到并发写的可能性高数据,对外界的修改采取保守策略,它认为线程很容易会把数据修改掉,因此在整个数据被修改的过程中都会采取锁定状态,直到一个线程使用完,其他线程才可以继续使用
悲观锁实现:synchronized关键字、Lock的实现类
适用场景:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量
3、独占锁和共享锁
独占锁:属于悲观锁,独占锁锁定的资源只允许进行锁定操作的程序使用,其它任何对它的操作均不会被接受,synchronized和ReentrantLock都是独占锁的实现
共享锁:属于乐观锁,共享锁锁定的资源可以被其它用户读取,但其它用户不能修改它,的读锁是可以被共享的,但是它的写锁确每次只能被独占,ReadWriteLock接口是共享锁的实现
4、可重入锁
可重入锁也叫递归锁,指的是同一个线程,如果外面的函数拥有此锁之后,内层的函数也可以继续获取该锁。在 Java 语言中 ReentrantLock 和 synchronized 都是可重入锁,
5、死锁
多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进
产生条件:互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链
避免方法:资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
解除死锁:剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等
死锁检测:Jstack命令、JConsole工具
6、公平锁、非公平锁
公平锁:线程需要按照请求的顺序来获得锁
非公平锁:允许“插队”的情况存在,程在发送请求的同时该锁的状态恰好变成了可用,那么此线程就可以跳过队列中所有排队的线程直接拥有该锁,ReentrantLock、synchronized 默认都是非公平锁的实现
公平锁由于有挂起和恢复所以存在一定的开销,性能不如非公平锁
7、volatile关键字、synchronized、ReentrantLock、ReadWriteLock、Lock接口
volatile关键字:
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取
volatile仅能使用在变量级别
volatile仅能实现变量的修改可见性,不能保证原子性
volatile不会造成线程的阻塞
volatile标记的变量不会被编译器优化
synchronized:
synchronized是通过 JVM 隐式实现的,synchronized 只允许同一时刻只有一个线程操作资源
synchronized是和if、else、for、while一样的关键字
synchronized的锁可重入、不可中断、非公平
synchronized是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住
synchronized可以使用在变量、方法、和类级别的
synchronized可以保证变量的修改可见性和原子性
synchronized可能会造成线程的阻塞
synchronized标记的变量可以被编译器优化
synchronized操作的是对象头中mark word
synchronized锁适合代码少量的同步问题
ReentrantLock:
ReentrantLock是类,实现Lock接口,可以被继承、可以有方法、可以有各种各样的类变量,基于 AQS(Abstract Queued Synchronizer,队列同步器)实现的,它默认是通过非公平锁实现的,在它的内部有一个 state 的状态字段用于表示锁是否被占用,如果是 0 则表示锁未被占用,此时线程就可以把 state 改为 1,并成功获得锁,而其他未获得锁的线程只能去排队等待获取锁资源
ReentrantLock需要手动加锁和释放锁,如果忘记释放锁,则会造成资源被永久占用
ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
ReentrantLock可以获取各种锁的信息
ReentrantLock可以灵活地实现多路通知
ReentrantLock底层调用的是Unsafe的park方法加锁
// 非公平锁
public ReentrantLock() {
sync = new NonfairSync();
} //可以设置为公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
} //通过lock()方法加锁,unlock() 方法解锁,可以知道当前是否获得锁
ReadWriteLock:
ReadWriteLock是一个接口,主要有两个方法,readLock()和writeLock()
ReadWriteLock需要通过实现读锁和写锁两个方法来用
读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的(排他的)
public interface ReadWriteLock { Lock readLock(); Lock writeLock();
}
Lock接口:
Lock代表实现类是ReentrantLock(可重入锁),支持语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则
Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁
Lock锁可重入、可判断、可公平(两者皆可)
Lock锁适合大量同步的代码的同步问题
public interface Lock {
// 获取锁
void lock();
// 如果当前线程未被中断,则获取锁,可以响应中断
void lockInterruptibly() throws InterruptedException;
// 返回绑定到此 Lock 实例的新 Condition 实例
boolean tryLock();
// 仅在调用时锁为空闲状态才获取该锁,可以响应中断
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
void unlock();
// 释放锁
Condition newCondition();
}
Java基础——锁的更多相关文章
- java基础 - 锁
------------------------ 参考: https://www.cnblogs.com/hustzzl/p/9343797.html https://blog.csdn.net/qq ...
- JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题
JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...
- Java基础教程:多线程杂谈——双重检查锁与Volatile
Java基础教程:多线程杂谈——双重检查锁与Volatile 双重检查锁 有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化.此时程序员可能会采用延迟初始化.但要正确实 ...
- 并发王者课-铂金1:探本溯源-为何说Lock接口是Java中锁的基础
欢迎来到<并发王者课>,本文是该系列文章中的第14篇. 在黄金系列中,我们介绍了并发中一些问题,比如死锁.活锁.线程饥饿等问题.在并发编程中,这些问题无疑都是需要解决的.所以,在铂金系列文 ...
- [Java面经]干货整理, Java面试题(覆盖Java基础,Java高级,JavaEE,数据库,设计模式等)
如若转载请注明出处: http://www.cnblogs.com/wang-meng/p/5898837.html 谢谢.上一篇发了一个找工作的面经, 找工作不宜, 希望这一篇的内容能够帮助到大 ...
- 最适合作为Java基础面试题之Singleton模式
看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...
- Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- Java基础应用
Java集合类解析 List.Map.Set三个接口,存取元素时,各有什么特点? List 以特定次序来持有元素,可有重复元素.Set 无法拥有重复元素,内部排序.Map 保存key-value值,v ...
- Java基础常见英语词汇
Java基础常见英语词汇(共70个) ['ɔbdʒekt] ['ɔ:rientid]导向的 ['prəʊɡræmɪŋ]编程 OO: object ...
随机推荐
- Python测试函数运行时间
import time import datetime # 测试函数运行时间 def cal_time(fn): """计算性能的修饰器""" ...
- 同事跳槽阿里P7,甩我一份微服务架构设计模式文档,看完我也去
给所有微服务架构开发者的忠告,我想对你们说: 第一,要记住微服务不是解决所有问题的万能“银弹”. 第二,编写整洁的代码和使用自动化测试至关重要,因为这是现代软件开发的基础. 第三,关注微服务的本质,即 ...
- Flutter —布局系统概述
老孟导读:此篇文章非常详细的讲解了 Flutter 布局系统的工作原理. 翻译自:https://itnext.io/flutter-layout-system-overview-c70bbe9ba9 ...
- Java-学习日记(100 == 100为true,1000 == 1000却为false?)
Integer底层设计 100 == 100为true,1000 == 1000却为false? 之前也写过String的==与equals的注意点,这次写下Integer的底层设计,不妨先运行下下面 ...
- nginx如何限制并发连接请求数?
简介 限制并发连接数的模块为:http_limit_conn_module,地址:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.ht ...
- 【jmespath】—3. 进阶 Object Projections
继续,来看Object Projections. 一.Object Projections 上面说的是列表投影,只适用于列表.那么对于json对象,可以用对象投影. 投影最终返回的仍然是个列表,只不过 ...
- jkd1.8 stream
目录 Stream 创建流 通过集合创建,例如Map (常用) 通过数组方式创建 通过Stream静态方法创建 中间操作 筛选和切片 filter limit skip distinct 映射 map ...
- 深入了解Netty【一】BIO、NIO、AIO简单介绍
引言 在Java中提供了三种IO模型:BIO.NIO.AIO,模型的选择决定了程序通信的性能. 1.1.使用场景 BIO BIO适用于连接数比较小的应用,这种IO模型对服务器资源要求比较高. NIO ...
- 2020重新出发,NOSQL,Redis主从复制
Redis主从复制 尽管 Redis 的性能很好,但是有时候依旧满足不了应用的需要,比如过多的用户进入主页,导致 Redis 被频繁访问,此时就存在大量的读操作. 对于一些热门网站的某个时刻(比如促销 ...
- 无法从NVIDA官网下载安装CUDA安装包?NVIDA官网怎么了?
最近几天由于不知名的原因,导致很多人无法从官网下载NVIDA的CUDA安装包,下载时,浏览器提示此文件可能危害你的计算机,选择保留下载下来也只是一个42字节的exe文件 双击进行安装又出现以下问题: ...