8. 读写锁

news/2024/5/20 6:51:03 标签: java, 并发, juc, 多线程

回顾悲观锁和乐观锁的概念
悲观锁: 见字知意,他是干什么都很悲观,所以在操作的时候,每次都上锁,使用时解锁
乐观锁:他很乐观,多线程,并不上锁,但是会发生 线程安全问题


表锁:整个表操作,不会发生死锁
行锁:每个表中的单独一行进行加锁,会发生死锁
读锁:共享锁(可以有多个人读),会发生死锁
写锁:独占锁(只能有一个人写),会发生死锁

关于读写锁

读写锁:一个资源可以被多个读线程访问,也可以被一个写线程访问,但不能同时存在读写线程,读写互斥,读读共享

读写锁ReentrantReadWriteLock
读锁为ReentrantReadWriteLock.ReadLock,readLock()方法
写锁为ReentrantReadWriteLock.WriteLock,writeLock()方法

创建读写锁对象private ReadWriteLock rwLock = new ReentrantReadWriteLock();
写锁 加锁 rwLock.writeLock().lock();,解锁为rwLock.writeLock().unlock();
读锁 加锁rwLock.readLock().lock();,解锁为rwLock.readLock().unlock();
 

 案例分析:
模拟多线程在map中取数据和读数据

java">//资源类
class MyCache {
    //创建map集合
    private volatile Map<String,Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }

    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }
        TimeUnit.MICROSECONDS.sleep(300);
        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

总结锁的演变

    无锁多线程抢夺资源
    synchronized和ReentrantLock,都是独占,每次只可以一个操作,不能共享
    ReentrantReadWriteLock,读读可以共享,提升性能,但是不能多人写。缺点:造成死锁(一直读,不能写),读进程不能写,写进程可以读。
    写锁降级为读锁(一般等级写锁高于读锁)

具体第四步演练的代码
具体降级步骤
获取写锁->获取读锁->释放写锁->释放读锁

java">//演示读写锁降级
public class Demo1 {
    public static void main(String[] args) {
        //可重入读写锁对象
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//读锁
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//写锁
        //锁降级
        //1 获取写锁
        writeLock.lock();
        System.out.println("manongyanjiuseng");
        //2 获取读锁
        readLock.lock();
        System.out.println("---read");
        //3 释放写锁
        writeLock.unlock();
        //4 释放读锁
        readLock.unlock();
    }
}

 如果是读之后再写,执行不了    因为读锁权限小于写锁 需要读完之后释放读锁,在进行写锁

java">//2 获取读锁
readLock.lock();
System.out.println("---read");
//1 获取写锁
writeLock.lock();
System.out.println("manongyanjiuseng");

http://www.niftyadmin.cn/n/1712921.html

相关文章

字符数组与字符指针的理解

1、字符数组 先来想一想什么是字符串&#xff1a; 字符串&#xff08;character string&#xff09;是一个或多个字符的序列&#xff0c;如下所示&#xff1a; “I am a good boy ”双引号不是字符串的一部分。双引号仅告知编译器它括起来的是字符串&#xff0c;正如单引号用于…

图的深度优先遍历和广度优先遍历的实现

1、 图的定义和术语 图&#xff08;grath&#xff09;由一个顶点&#xff08;vertex&#xff09;的有穷非空集合V&#xff08;G&#xff09;和一个弧&#xff08;arc&#xff09;的集合E(G)组成&#xff0c;通常记作G (V,E)。图中的顶点就是数据结构中的数据元素&#xff0c;…

9. 阻塞队列

阻塞队列是共享队列&#xff08;多线程操作&#xff09;&#xff0c;一端输入&#xff0c;一端输出 不能无限放队列&#xff0c;满了之后就会进入阻塞&#xff0c;取出也同理 当队列是空的&#xff0c;从队列中获取元素的操作将会被阻塞 当队列是满的&#xff0c;从队列中添…

数据结构————线性表的实现 合并

1、表的实现 1.1表的数组表示 线性表的定义&#xff1a;线性表是n个元素的有限序列&#xff0c;表中各个数据元素具有相同特性&#xff0c;属于同一数据对象。 #include <stdio.h> #include <stdlib.h>#define TRUE 1 #define FALSE 0 #define OK 1 #define ERR…

10. 线程池

连接池是创建和管理一个连接的缓冲池的技术&#xff0c;这些连接准备好被任何需要它们的线程使用 线程池&#xff08;英语&#xff1a;thread pool&#xff09;一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&am…

数据结构———插入排序(希尔排序,折半插入,直接插入)

排序 直接插入排序 直接插入排序。对于少量元素的排序&#xff0c;它是一个有效的算法 。插入排序是一种最简单的排序方法&#xff0c;它的基本思想是将一个记录插入到已经排好序的有序表中&#xff0c;从而一个新的、记录数增1的有序表。在其实现过程使用双层循环&#xff0…

11. Fork与Join分支

将一个大的任务拆分成多个子任务进行并行处理&#xff0c;最后将子任务结果合并成最后的计算结果 该算法相当于递归&#xff0c;且是二分查找思路 八种基本排序问题 &#xff08;第六篇 归并排序&#xff09;图文详解_想成为大神说32的博客-CSDN博客 class Fibonacci extends…

数据结构——交换排序(冒泡排序和快速排序。)

交换排序 所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动。 冒泡排序 1、比较相邻的元素。如果第一个比第…