JUC读写锁和阻塞队列

目录

读写锁

阻塞队列


读写锁

读写锁是一种并发控制机制,通过区分读操作和写操作,允许更高的并发性。

核心特点:

读锁(共享):可被多个读线程同时持有,前提是无写线程。

写锁(独占):同一时刻只能被一个写线程持有。

关键保证:

成功获取读锁的线程,能够看到之前写锁释放时所做的所有更新(内存可见性)。

适用场景:

理想情况:数据初始化后,读操作频繁、写操作极少(如目录服务、缓存)。

不适合情况:写操作频繁、读操作过短,或单处理器环境(开销可能大于收益)。

使用实例:

java 复制代码
package com.qcby.readWriteLock;

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

public class Test {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 0; i < 5; i++) {
            final int j = i;
            new Thread(() -> {
                myCache.put(j + "", j);
                myCache.get(j + "");
            }, String.valueOf(i)).start();
        }
// ---------------------------- 加锁的版本 ------------------------------------
//        MyCacheLock myCacheLock = new MyCacheLock();
//        for (int i = 0; i < 5; i++) {
//            final int j = i;
//            new Thread(() -> {
//                myCacheLock.put(j + "", j);
//                myCacheLock.get(j + "");
//            }, 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() + "写入..........");
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "写入已经完成....");
    }

    //读取缓存操作
    public void get(String key){
        System.out.println(Thread.currentThread().getName() + "读取..........");
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取已经完成....");
    }
}

/**
 * 加锁的类
 */
class MyCacheLock{
    private volatile Map<String, Object> map = new HashMap<>();

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //写入缓存操作
    public void put(String key, Object value){

        readWriteLock.writeLock().lock(); // 加写锁
        try {
            System.out.println(Thread.currentThread().getName() + "写入..........");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入已经完成....");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            readWriteLock.writeLock().unlock(); // 解写锁
        }
    }

    //读取缓存操作
    public void get(String key){

        readWriteLock.readLock().lock(); // 加读锁
        try {
            System.out.println(Thread.currentThread().getName() + "读取..........");
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取已经完成....");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            readWriteLock.readLock().unlock(); // 解读锁
        }
    }
}

在我们加锁之前很明显在某个线程写数据的时候,其他线程也可以同时写入数据,操作并不是原子性的。

在0写入完成之前,有其他线程执行了其他操作。

加上读写锁的时候可以实现更细粒度的并发控制,读的时候可以多线程读,写的时候只能一个线程写:

阻塞队列

|--------|-----------|------------|----------|----------------------------|
| 方法 | 抛出异常 | 不抛出异常,有返回值 | 阻塞等待 | 限时等待 |
| 插入 | add(val) | offer(val) | put(val) | offer(val, time, TimeUnit) |
| 删除 | remove() | poll() | take() | poll(time, TimeUnit) |
| 判断首个元素 | element() | peek() | - | - |

java 复制代码
    /**
     * 使用了ArrayBlockingQueue add和remove
     * add超过了队列大小会添加元素抛出异常  Queue full
     * remove在队列为空时移除元素抛出异常  java.util.NoSuchElementException
     * element在队列为空时查看队首元素抛出异常 java.util.NoSuchElementException
     */
    public static void willException(){

        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.add("0"));
        System.out.println(arrayBlockingQueue.add("1"));
        System.out.println(arrayBlockingQueue.add("2"));
        // 超过了队列大小会抛出异常  Queue full
        // arrayBlockingQueue.add("1");
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        // 队列里为空会抛出异常 java.util.NoSuchElementException
//        System.out.println(arrayBlockingQueue.remove());

        // 查看队首元素 在队列为空时查看队首元素抛出异常 java.util.NoSuchElementException
//        System.out.println(arrayBlockingQueue.element());
    }
java 复制代码
    /**
     * 使用了使用了ArrayBlockingQueue offer 和 poll
     * offer 在队伍满时添加元素不抛出异常,会返回false
     * poll 在队列空时移除元素不抛出异常,会返回null
     * peek 队列为空时不会抛出异常 返回null
     */
    public static void noException(){
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer("0"));
        System.out.println(arrayBlockingQueue.offer("1"));
        System.out.println(arrayBlockingQueue.offer("2"));
        // 超过了队列大小不会抛出异常 返回false
//        System.out.println(arrayBlockingQueue.offer("1"));
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // 队列里为空不会抛出异常 返回null
//        System.out.println(arrayBlockingQueue.poll());

        // 查看队首元素 队列为空时返回null
        System.out.println(arrayBlockingQueue.peek());
    }
java 复制代码
    /**
     * 阻塞等待,等一段时间
     * offer(val, time, TimeUnit) 如果队列满了再放置元素,会等待 参数 时间,如果还是慢的就返回false
     * poll(time, TimeUnit) 如果队列空再取出元素, 会等待 参数 时间, 如果还是空的就返回null
     */
    public static void blockingWaitingWithTime() throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        arrayBlockingQueue.offer("1");
        arrayBlockingQueue.offer("2");
        arrayBlockingQueue.offer("3");
        //此时队列满了,它等一秒钟就不等了,返回false,不抛出异常
        System.out.println(arrayBlockingQueue.offer("4", 1, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // 此时队列没元素了,等一秒钟就不等了,返回null,不抛出异常
        System.out.println(arrayBlockingQueue.poll(1, TimeUnit.SECONDS));
    }
java 复制代码
    /**
     * 阻塞等待,等一段时间
     * offer(val, time, TimeUnit) 如果队列满了再放置元素,会等待 参数 时间,如果还是慢的就返回false
     * poll(time, TimeUnit) 如果队列空再取出元素, 会等待 参数 时间, 如果还是空的就返回null
     */
    public static void blockingWaitingWithTime() throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        arrayBlockingQueue.offer("1");
        arrayBlockingQueue.offer("2");
        arrayBlockingQueue.offer("3");
        //此时队列满了,它等一秒钟就不等了,返回false,不抛出异常
        System.out.println(arrayBlockingQueue.offer("4", 1, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // 此时队列没元素了,等一秒钟就不等了,返回null,不抛出异常
        System.out.println(arrayBlockingQueue.poll(1, TimeUnit.SECONDS));
    }
相关推荐
太阳之神aboluo3 小时前
RabbitMQ
java·分布式·spring·rabbitmq·java-rabbitmq
qqty12173 小时前
windows配置永久路由
java
weixin_404157683 小时前
Java高级面试与工程实践问题集(七)
java·开发语言·面试
Yupureki3 小时前
《Linux系统编程》13.Ext系列文件系统
linux·运维·服务器·c语言·开发语言·c++
计算机学姐3 小时前
基于SpringBoot+Vue的智能民宿预定游玩系统【AI智能客服+数据可视化】
java·vue.js·spring boot·后端·mysql·spring·信息可视化
wenlonglanying3 小时前
springcloud springboot nacos版本对应
spring boot·spring·spring cloud
布伦鸽3 小时前
C#检测文本编码格式
开发语言·c#
骇客野人3 小时前
JDK8和JDK8以后对jdk的优化,以及为什么如此优化
java·开发语言·windows