JUC系列(五) 读写锁与阻塞队列

简介: 阻塞队列和读写锁 都是很有作用的多线程工具 有所了解 在需要的时候也可以投入到业务中使用
📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务netty单点登录SSMSpringCloudAlibaba
😝公众号😝想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
当前专栏JUC系列

读写锁

Synchronized存在一个性能问题就是不同读取之间互斥,我们想要实现的最好效果是可以做到读和读互不影响,写的时候只有一个线程能写

解决方案 : ReadWriteLock。

案例代码
package rwLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @projectName: JUC
 * @package: rwLock
 * @className: rwLockDemo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/2 16:29
 * @version: 1.0
 */
public class rwLockDemo {
    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++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}

/**
 * 自定义缓存类
 * 加锁
 * */
class MyCache {
    //存放数据的集合
    private volatile Map<String, Object> map = new HashMap<>();

    //    存 写
    public void put(String key, Object value) {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完毕");
    }
    //    取 读
    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "读取" + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取" + "");
    }
}

image-20220302170621703

可以看到这并不是我们想要的效果,这个时候我们需要加锁

ReadWriteLock读写锁 分别有

readLock()读锁

writeLock()写锁

使用方式除了相比lock细化的一些其他没有变化

读写锁代码实例

思路理解 :

独占锁(写锁)

共享锁(读锁)

public class rwLockDemo {
    public static void main(String[] args) {
        //MyCache myCache = new MyCache();
        MyCacheLock myCache = new MyCacheLock();
        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++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}
class MyCacheLock {
    //存放数据的集合
    private volatile Map<String, Object> map = new HashMap<>();

    //读写锁的区别, 更加细粒度的控制
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //    存 写
    public void put(String key, Object value) {
        //加入写锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            readWriteLock.writeLock().unlock();
        }
    }

    //    取 读
    public void get(String key) {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取" + "");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

输出效果就达到了,先写且只有一个写,之后随意读

image-20220302182538999

阻塞队列

阻塞队列简介

什么是阻塞队列,我们要分开来理解

阻塞: 等待前面的走了才能加入新的

队列: 先进来的,先出去

image-20220302183347089

阻塞队列 在jdk文档中的 解释

image-20220302183907308

队列接口

我们学习的BlockingQueue也是实现类之一

什么时候我们会使用 阻塞队列

多线程 , 线程池 用的相对的多一点

image-20220302184326096

队列的类关系图

image-20220302184516163

阻塞队列相对的四组api

  1. 抛出异常api

        /** 会抛出异常的
         * java.lang.IllegalStateException: Queue full 会抛出队列已经满了的异常
         * java.util.NoSuchElementException  过多移除异常
         * */
        public static void test1() {
            ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println("===============多过加入================");
            System.out.println(blockingQueue.add("a"));
            System.out.println(blockingQueue.add("b"));
            System.out.println(blockingQueue.add("c"));
    
            ////    此时的队列长度为 3 如果我们此时加入 第四个会怎么样,抛出队列已经满了的异常
            //System.out.println(blockingQueue.add("b"));
            System.out.println("===============过多移除================");
            System.out.println(blockingQueue.remove());
            System.out.println(blockingQueue.remove());
            System.out.println(blockingQueue.remove());
            System.out.println(blockingQueue.remove());
        }
  2. 不会抛出异常api

    public static void test2() {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println("===============多过加入================");
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        //返回false
        System.out.println(blockingQueue.offer("d"));
        System.out.println("===============过多移除================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //返回null
        System.out.println(blockingQueue.poll());
    
    }
  3. 阻塞等待 api

    /* 一直等待 阻塞
     * */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put 队列没有位置了 一支阻塞
        //blockingQueue.put("d");
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        //m没有这个元素一直等待
        System.out.println(blockingQueue.take());
    }
  4. 超时等待 api

    /*等待
    等待超时*/
    public static void test4() throws InterruptedException {
        //队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        //等待,如果设置时间还没有空位置。否则结束
        blockingQueue.offer("d", 2, TimeUnit.SECONDS);
        System.out.println("======================");
        blockingQueue.poll();
        blockingQueue.poll();
        blockingQueue.poll();
        //等待,如果设置时间还没有找到。否则结束
        blockingQueue.poll(2, TimeUnit.SECONDS);
    
    }
方式 抛出异常 有返回值 阻塞等待 超时等待
添加操作 add offer put() offer()
移除操作 remove poll take() poll()
判断队列首元素 element peek

同步队列

特性

同步队列,SynchronusQueue 同步队列 和其他的 BlockingQueue不一样 SynchronusQueue不存储元素

put了 一个元素 必须先从里面拿出来,否则是不能再put进去值

代码实例
public class synchronusQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
        new Thread(() -> {

            try {
                System.out.println(Thread.currentThread().getName() + "put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T1").start();
        new Thread(() -> {

            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();
    }
}
相关文章
|
3月前
|
安全 Java
JUC锁: ReentrantReadWriteLock详解
`ReentrantReadWriteLock` 主要用于实现高性能的并发读取,而在写操作相对较少的场景中表现尤为突出。它保证了数据的一致性和线程安全,在合适的场合合理使用 `ReentrantReadWriteLock`,可以实现更加细粒度的控制,并显著提升应用性能。然而,需要注意它的复杂度较一般的互斥锁高,因此在选择使用时要仔细考虑其适用场景。
41 1
|
7月前
|
安全 Java API
多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)
多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)
61 4
|
6月前
|
SQL 安全 Java
JUC多线程-线程池-Thredalocal-CAS-AQS-死锁
JUC多线程-线程池-Thredalocal-CAS-AQS-死锁
|
Java
多线程和并发编程(3)—AQS和ReentrantLock实现的互斥锁
多线程和并发编程(3)—AQS和ReentrantLock实现的互斥锁
111 0
|
消息中间件 缓存 Java
多线程应用——阻塞队列
用数组实现阻塞队列
67 0
|
存储 消息中间件 算法
JUC-阻塞队列
问题引出 一.单端阻塞队列(BlockingQueue) 二.双端阻塞队列(BlockingDeque) 三.延迟队列(DelayQueue)
52 0
|
JavaScript 小程序 Java
JUC多线程:CountDownLatch、CyclicBarrier、Semaphore 同步器原理 上
JUC多线程:CountDownLatch、CyclicBarrier、Semaphore 同步器原理 上
|
消息中间件 JavaScript 小程序
JUC多线程:CountDownLatch、CyclicBarrier、Semaphore 同步器原理 下
JUC多线程:CountDownLatch、CyclicBarrier、Semaphore 同步器原理 下
|
存储 前端开发 安全
【JUC并发编程】 详解锁与队列
【JUC并发编程】 详解锁与队列
147 0
【JUC并发编程】 详解锁与队列