【并发技术12】线程锁技术的使用
2021-03-19 03:27
标签:gen 基础 math 传统 process eval val start 任务 这个例子和前面介绍 synchronized 的例子差不多,区别在于将 synchronized 改成了 lock。从程序中可以看出,使用 Lock 的时候,需要先 new 一个 Lock 对象,然后在线程任务中需要同步的地方上锁,但是一定要记得放锁,所以使用 try 块去处理了一下,将放锁的动作放在 finally 块中了。 这是一个线程任务的情况,如果两个线程任务也不麻烦,还是在这个类中新建一个任务方法,因为 Lock 是这个类的成员变量,还是可以用这个 lock,而且必须用这个 lock,因为要实现同步互斥,必须使用同一把锁。 锁又分为读锁和写锁,读锁与读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由 jvm 自己控制的。这很好理解,读嘛,大家都能读,不会对数据造成修改,只要涉及到写,那就可能出问题。我们写代码的时候只要在挣钱的位置上相应的锁即可。读写锁有个接口叫 ReadWriteLock,我们可以创建具体的读写锁实例,通过读写锁也可以拿到读锁和写锁。下面看一下读写锁的例子。 为了说明读锁和写锁的特点(读锁和读锁不互斥,读锁与写锁互斥,写锁与写锁互斥),我先把上面两个任务方法中上锁和放锁的四行代码注释掉,来看一下运行结果。 其实不管是注释调读锁还是注释调写锁,还是全注释掉,都会出问题,写的时候会有线程去读。那么将读写锁加上后,再看一下运行结果。 可以看出,有了读写锁,各个线程运行有序,从结果来看,也印证了读锁和读锁不互斥,写锁与读锁、写锁都互斥的特点。 现在使用读写锁写一个模拟缓存数据的 demo,实现功能如下:现在有5个线程都需要拿数据,一开始是没有数据的,所以最先去拿数据的那个线程发现没数据,它就得去初始化一个数据,然后其他线程拿数据的时候就可以直接拿了。代码如下。 从代码中可以看出,在 processCache 方法中对读锁和写锁的交替使用。一开始进来都是读数据的,所以一开始都是上了读锁,但是当第一个线程进来发现没有缓存数据的时候,它得写数据,那么此时它得先把读锁给释放掉,换了把写锁,告诉其他线程:“哥们,这里面根本没数据啊,我们被坑了,让我先弄个数据来吧,你们等会儿~”,等该线程初始化好了数据后,其他线程就可以读了,于是它又把读锁装起来了,把写锁释放了,然后它出去了。这就模拟了拿缓存数据的一个 demo,可以看出,在一个方法中,同一个线程可以操作两个锁的。看一下运行结果。 Thread-1: no cache! 这和 Hibernate 中的那个 load(id, Class.class) 方法有点类似,先拿到的是代理对象,要使用该对象的时候,如果发现没有,就新产生一个,如果有了就直接拿来用。 继续进阶,如果现在要缓存多个数据,即要写一个缓存系统,那该如何做呢?一个缓存系统无非就是一个容器,可以存储很多缓存数据,很自然的想到使用一个 Map,专门装缓存数据,然后供多个线程去使用。所以整个涉及思路,跟上面缓存单个数据是一样的,不过就是多考了一些东西而已,看下代码。 整个代码的结构和上面的一样,理解了缓存单个数据后,这个代码也不难理解。这里只是个 demo,实际中可以是跟数据库打交道,第一次从缓存中拿肯定是没有的,那么就要去数据库中查,然后把取到的数据放到缓存中,下次别的线程来就能直接从缓存中取了。看一下运行结果。 Thread-0 write cache for cache1 从结果中可以看出,线程 0 首先去缓存中拿 key 为 cache1 的值,没拿到,往里面写了一个,然后线程 4 去缓存中拿 key 为 cache2 的值也没拿到,于是也写了一个,在此期间线程 0 把值拿了出来,后面几个线程也随后陆续的拿出来了。读写锁的应用还是很广泛的,而且很好用,就总结这么多吧。 微服务架构盛行的时代,你需要了解点 Spring Boot 如果觉得对您有帮助,请转发给更多人吧~ 【并发技术12】线程锁技术的使用 标签:gen 基础 math 传统 process eval val start 任务 原文地址:https://blog.51cto.com/14989612/25484211. Lock的简单使用
有了synchronized 的基础,Lock 就比较简单了,首先看一个实例:
public class LockTest {
public static void main(String[] args) {
new LockTest().init();
}
private void init() {
final Outputer outputer = new Outputer();
// 线程1打印:duoxiancheng
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("duoxiancheng");
}
}
}).start();
;
// 线程2打印:eson15
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("eson15");
}
}
}).start();
;
}
// 自定义一个类,保存锁和待执行的任务
static class Outputer {
Lock lock = new ReentrantLock(); //定义一个锁,Lock是个接口,需实例化一个具体的Lock
//字符串打印方法,一个个字符的打印
public void output(String name) {
int len = name.length();
lock.lock();
try {
for (int i = 0; i
2. 读写锁的妙用
2.1 读写锁的基本用法
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue3 q3 = new Queue3(); //封装共享的数据、读写锁和待执行的任务的类
for (int i = 0; i
2.2 读写锁用于缓存数据
public class ReadWriteLockTest2 {
public static void main(String[] args) {
CacheData cache = new CacheData();
for(int i = 1; i
Thread-1: already cached!
Thread-1 get data: 893
Thread-0 get data: 893
Thread-2 get data: 8932.3 读写锁用于缓存系统
public class CacheDemo {
public static void main(String[] args) {
Cache cac = new Cache();
for(int i = 0; i cache = Collections.synchronizedMap(new HashMap
Thread-0 has already written cache!
Thread-4 write cache for cache2
Thread-0: aaa1464782404722
Thread-4 has already written cache!
Thread-4: aaa1464782404723
Thread-3: aaa1464782404723
Thread-2: aaa1464782404722
Thread-1: aaa1464782404722
Thread-5: aaa1464782404723更多推荐阅读:
读一篇故事,交一个朋友~
Java干货视频资源
web前端干货视频资源
资源大放送!!!永久有效,持续更新(必收藏系列)
【整理分享】你需要的资源在这里,拿好不谢
这里有技术、有段子、有生活、有资源
↓↓↓