博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程 之锁
阅读量:2455 次
发布时间:2019-05-10

本文共 6001 字,大约阅读时间需要 20 分钟。

文章目录

公平锁

类似排队打饭,先来后到,多个线程按照申请锁的顺序来获取锁。

非公平锁

无需排队可以加塞,多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象。

非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言也是一种非公平锁。

可重入锁(又名递归锁)ReentrantLock

可重入锁,也叫递归锁。

同一线程外层函数获得锁之后,内存递归函数任然能获取该锁的代码。
在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
也就是说,线程可以进入任何一个它已拥有的锁所同步着的代码块。

ReentrantLock通过构造函数指定该锁是否是公平锁 ,默认是非公平锁。

Synchronized 可重入案例:

public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone(); new Thread(() -> {
try {
phone.sendSms(); } catch (Exception e) {
e.printStackTrace(); } }, "t1").start(); new Thread(() -> {
try {
phone.sendSms(); } catch (Exception e) {
e.printStackTrace(); } }, "t2").start(); }}class Phone {
public synchronized void sendSms() throws Exception {
System.out.println(Thread.currentThread().getName() + "\t sendSms"); sendEmail(); } public synchronized void sendEmail() throws Exception {
System.out.println(Thread.currentThread().getName() + "\t sendEmail"); }}

ReentrantLock 可重入案例:

public class ReentrantLockDemo {
public static void main(String[] args) {
Apple apple = new Apple(); new Thread(apple, "t1").start(); new Thread(apple, "t2").start(); new Thread(apple, "t3").start(); }}class Apple implements Runnable{
private Lock lock = new ReentrantLock(); @Override public void run() {
get(); set(); } private void get(){
lock.lock(); try {
System.out.println(Thread.currentThread().getName() + "\t get"); }finally {
lock.unlock(); } } private void set(){
lock.lock(); try {
System.out.println(Thread.currentThread().getName() + "\t set"); }finally {
lock.unlock(); } }}

自旋锁

CAS是一个典型的自旋锁思想。

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

自旋锁好处:循环比较获取直到成功为止,没有类似wait的阻塞。

通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B随后进来后发现当前有线程持有锁,不是null,所以只能通过自旋等待,直到A释放锁后B随后抢到。

public class SpinLockDemo {
AtomicReference
atomicReference = new AtomicReference<>(); public void myLock() {
Thread thread = Thread.currentThread(); System.out.println(thread.getName() + "\t come in"); // 这里循环获取等待期望的值,自旋 while (!atomicReference.compareAndSet(null, thread)) {
} } public void myUnLock() {
Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(thread.getName() + "\t invoked myUnLock"); } 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(); }, "AA").start(); try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); } new Thread(() -> {
spinLockDemo.myLock(); try {
TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); } spinLockDemo.myUnLock(); }, "BB").start(); }}

独占锁(写锁)、共享锁(读锁)、互斥锁

独占锁: 指该锁一次只能被一个线程所持有。对ReentrantLockSynchronized而言都是独占锁。

共享锁: 值该锁可被多个线程所持有。

ReentrantReadWriteLock 其读锁是共享的,其写锁是独占锁。

读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。

读写锁案例:

import java.util.HashMap;import java.util.Map;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantReadWriteLock;// 资源类class MyCaChe {
// 保证可见性 private volatile Map
map = new HashMap<>(); private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); public void put(String key, Object value) {
reentrantReadWriteLock.writeLock().lock(); try {
System.out.println(Thread.currentThread().getName() + "\t正在写入" + key); //模拟网络延时 try {
TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) {
e.printStackTrace(); } map.put(key, value); System.out.println(Thread.currentThread().getName() + "\t写入完成"); }finally {
reentrantReadWriteLock.writeLock().unlock(); } } public void get(String key) {
reentrantReadWriteLock.readLock().lock(); try {
System.out.println(Thread.currentThread().getName() + "\t正在读取"); //模拟网络延时 try {
TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) {
e.printStackTrace(); } Object result = map.get(key); System.out.println(Thread.currentThread().getName() + "\t正在完成" + result); }finally {
reentrantReadWriteLock.readLock().unlock(); } }}/** * 多个线程同时操作一个资源类没有任何问题,所以为了满足并发量, * 读取共享资源应该可以同时进行。 * 但是如果有一个线程想去写共享资源,就不应该再有其他线程可以对该资源进行读或写。 * 总结: * 读-读 能共存 * 读-写 不能共存 * 写-写 不能共存 * 写操作 原子+独占,整个过程必须是一个完整统一的整体,中间不允许被分割、被打断。 */public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCaChe myCaChe = new MyCaChe(); for (int i = 1; i <= 5; i++) {
final int temp = i; new Thread(() -> {
myCaChe.put(temp + "", temp); }, String.valueOf(i)).start(); } for (int i = 1; i <= 5; i++) {
int finalI = i; new Thread(() -> {
myCaChe.get(finalI + ""); }, String.valueOf(i)).start(); } }}

转载地址:http://sfchb.baihongyu.com/

你可能感兴趣的文章
golang底层深入_带有Golang的GraphQL:从基础到高级的深入研究
查看>>
如何选择正确的容器编排以及如何进行部署
查看>>
出现字迹模糊迹象_改变迹象:如何使用动态编程解决竞争性编程问题
查看>>
angular 渐进_如何创建具有Angular和无头CMS的渐进式Web应用程序
查看>>
Dash的快速入门将使您在5分钟内进入“ Hello World”
查看>>
用户体验改善案例_用户体验案例研究:建立更好的体验(重新设计“和平航空”网站)...
查看>>
nginx mozilla_我发现Mozilla的私人浏览模式存在重大缺陷。
查看>>
databricks_如何开始使用Databricks
查看>>
盖茨比乔布斯_如何使用盖茨比创建您的博客并通过手机进行处理
查看>>
如何使用React Native构建嵌套的抽屉菜单
查看>>
bdd cucumber_如何使用BDD构建坚如磐石的Ruby on Rails应用
查看>>
react发送和接收请求_React行为编程简介:请求,等待和阻止
查看>>
orcale可视化建立用户_建立动态可视化的新方法
查看>>
gis计算各省河流长度_用河流和各方解释安全漏洞
查看>>
代码编写工具_我希望在开始编写代码时就已经知道的工具:已复习
查看>>
把转变为json_如何使用7行JSON将您的网站转变为移动应用程序
查看>>
如何使用TensorFlow对象检测API播放Quidditch
查看>>
交付方式 saas_我在全职工作时如何交付我的第一个SaaS副项目
查看>>
instagram技术_Instagram9位科技女孩进行技术采访的主要技巧
查看>>
angular面试题及答案_关于最流行的Angular问题的StackOverflow上的48个答案
查看>>