Java中锁的简单使用体验
锁是控制多个线程访问共享资源的一种同步机制。
- synchronized:可以将代码块或方法设置为同步。
- ReentrantLock:提供了比synchronized更广泛的锁操作函数。
- ReadWriteLock:允许多个线程同时读共享资源,但只允许一个线程写共享资源。如ReentrantReadWriteLock。
- StampedLock:提供了乐观读锁,可以取代ReadWriteLock。
- Semaphore:信号量,可以控制同时访问的线程个数。
不同锁的应用场景:
- synchronized:适用于加锁代码块比较小的情况。
- ReentrantLock:需要更灵活的锁获取与释放、可中断锁、公平锁等高级功能时使用。
- ReadWriteLock:读多写少的场景,允许同时多个线程读。
- StampedLock:读多写少,需要乐观读锁时使用。
- Semaphore:限流,需要控制最大运行线程数时使用。
悲观锁和乐观锁是两种不同的锁机制:
悲观锁:
- 总是假设会发生冲突,因此在访问数据的时候都采取加锁的方式。这种锁机制下访问的数据都是互斥的。
- 典型的悲观锁是通过 synchronized 或者 ReentrantLock 等来实现。
- 访问时加锁,性能较差,但确保了数据访问的正确性。
乐观锁:
- 假设不会发生冲突,只在提交操作时检查是否违反数据完整性。
- 典型的乐观锁是通过版本号机制来实现。
- 更新的时候不加锁,只是检查版本号,性能较好。但数据可能会被覆盖。
- 适用于写比较少的场景下。
总结:
- 悲观锁性能差但安全,乐观锁性能好但有冲突的风险。
- 根据实际场景需要选择合适的锁机制。读多写少时建议使用乐观锁,写操作频繁时建议使用悲观锁。
使用如下:
synchronized
package com.demo1; import lombok.NonNull;
import lombok.Synchronized;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock; public class Lock implements Runnable {
private final ReentrantLock reentrantLock = new ReentrantLock();
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final StampedLock stampedLock = new StampedLock(); @Override
public void run() {
synchronized (Lock.class){
for (int i = 1; i < 100; i++) {
System.out.println(i);
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
//构建线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 2, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(2), new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable r) {
Thread t = new Thread(r);
t.setName("myThread");
return t;
}
}, new ThreadPoolExecutor.AbortPolicy());
Lock l = new Lock();
pool.submit(l);
pool.submit(l);
pool.shutdown();
}
}
ReentrantLock:
@Override
public void run() {
reentrantLock.lock();
for (int i = 1; i < 100; i++) {
System.out.println(i);
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
reentrantLock.unlock();
}
ReadWriteLock:
@Override
public void run() {
readWriteLock.readLock().lock();
for (int i = 1; i < 100; i++) {
System.out.println(i);
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
readWriteLock.readLock().unlock();
}
当使用读锁时,会出现如: 1 1 2 2 3 3这样交替的情况,但是写锁不会出现这种情况
StampedLock:
private final StampedLock lock = new StampedLock(); // 乐观读
public void optimisticRead() {
long stamp = lock.tryOptimisticRead();
// 执行读操作
if(!lock.validate(stamp)){//当出现数据不一致问题时进行升级
// 升级为悲观读锁
stamp = lock.readLock();
try {
// 重做读取操作
} finally {
lock.unlockRead(stamp);
}
}
} // 悲观写
public void pessimisticWrite() {
long stamp = lock.writeLock();
try {
// 写操作
} finally {
lock.unlockWrite(stamp);
}
}
Semaphore://只允许两个线程同时使用,类似c++的pv操作
private final Semaphore semaphore = new Semaphore(2);
@SneakyThrows
@Override
public void run() {
semaphore.acquire();
System.out.println("执行");
Thread.sleep(3000);
semaphore.release();
}
如果有什么错误,欢迎指正
Java中锁的简单使用体验的更多相关文章
- JAVA中锁的解决方案
前言 在上一节中,我们给大家介绍了什么是锁,以及锁的使用场景,我相信大家对锁的定义,以及锁的重要性都有了比较清晰的认识.在这一节中,我们会给大家继续做深入的介绍,介绍JAVA为我们提供的不同种类的锁. ...
- 并发王者课-铂金1:探本溯源-为何说Lock接口是Java中锁的基础
欢迎来到<并发王者课>,本文是该系列文章中的第14篇. 在黄金系列中,我们介绍了并发中一些问题,比如死锁.活锁.线程饥饿等问题.在并发编程中,这些问题无疑都是需要解决的.所以,在铂金系列文 ...
- 多线程(三) java中线程的简单使用
java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...
- 关于Java中数组的简单使用
关于java中数组的简单使用--继java环境配置后的第二篇学习笔记 近期在学习Java的过程中学到了数组的部分,至于为什么我会到数组才来写这个,主要是数组这一章节的内容感觉还是与之前学的C里面的数组 ...
- 浅谈对java中锁的理解
在并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性.synchronized机制是给共享 ...
- java中锁的理解
在并发编程中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,在java中synchronized关键字被常用于维护数据一致性.synchronized机制是给共享 ...
- java中锁的应用
锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized(重量级) 和 ReentrantLock(轻量级)等等 ) .这些已经写好提供的锁为我们开发提供了便利. ...
- JMM之Java中锁概念的分类总结
在Java的并发编程中不可避免的涉及到锁.从不同维护可以将锁进行不同的分类,如下: 1.乐观锁和悲观锁(根据读写操作的比例划分) 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数 ...
- java中锁的概念/介绍
前言 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率.本文旨在对锁相关源码(本文中的源码来自JDK 8和Netty 3.10.6).使用场景进行举例,为读者介绍 ...
- Java线程锁一个简单Lock
/** * @author * * Lock 是java.util.concurrent.locks下提供的java线程锁,作用跟synchronized类似, * 单是比它更加面向对象,两个线程执行 ...
随机推荐
- golang调用sdl2,播放pcm音频,报错signal arrived during external code execution。
golang调用sdl2,播放pcm音频,报错signal arrived during external code execution. win10 x64下测试成功,其他操作系统下不保证成功. 采 ...
- 2021-05-22:假设所有字符都是小写字母, 大字符串是str,arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次。使用arr中的单词有多少种拼接str的方式。 返回方法数。
2021-05-22:假设所有字符都是小写字母, 大字符串是str,arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次.使用arr中的单词有多少种拼接str的方式. 返回方法数. 福大大 ...
- 2022-01-04:一个无序数组长度为n,所有数字都不一样,并且值都在[0...n-1]范围上。 返回让这个无序数组变成有序数组的最小交换次数。 来自小红书。
2022-01-04:一个无序数组长度为n,所有数字都不一样,并且值都在[0-n-1]范围上. 返回让这个无序数组变成有序数组的最小交换次数. 来自小红书. 答案2022-01-04: 下标循环怼. ...
- 2021-11-29:给定一个单链表的头节点head,每个节点都有value(>0),给定一个正数m, value%m的值一样的节点算一类, 请把所有的类根据单链表的方式重新连接好,返回每一类的头节点
2021-11-29:给定一个单链表的头节点head,每个节点都有value(>0),给定一个正数m, value%m的值一样的节点算一类, 请把所有的类根据单链表的方式重新连接好,返回每一类的 ...
- 2021-09-05:单词搜索 II。给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。单词必须按照字母顺序,通过 相邻的
2021-09-05:单词搜索 II.给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词.单词必须按照字母顺序,通过 相邻的 ...
- Springcloud 开始来了解
为什么要学习springcloud? "微服务 "一词源于Martin Fowler 的名为 Microservices 的博文,简单地说, 微服务是系统架构上的一种设计风格, 它 ...
- CIO视角|平台工程带来的优势与机遇
在当今高速发展的技术环境中,企业越来越依赖技术作为创新和竞争优势的战略驱动力.首席信息官(CIO)在企业中负责监督信息和计算机技术的管理和实施,以交付预期的业务成果.在技术是业务核心的公司中,CIO ...
- python 学习之-----正则表达式
mport re'''# re 模块regex 正则表达式,正则表达式应用范围:1爬虫:2自动化运维--开发自动化:# 什么是正则表达式:一套规则: 匹配字符串的规则# 能做什么 1 检测一个输入的字 ...
- 在R中子集化数据框的5种方法
由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访问文中链接. 通常,我们在使用大型数据集时,只会对其中的一小部分感兴趣,用以进行特定分析. 那么,我们应该如 ...
- 使用CNI网络插件(calico)实现docker容器跨主机互联
目录 一.系统环境 二.前言 三.CNI网络插件简介 四.常见的几种CNI网络插件对比 五.Calico网络之间是如何通信的 六.配置calico让物理机A上的docker容器c1可以访问物理机B上的 ...