JUC(6):读写锁;阻塞队列

ReadWriteLock(读写锁)

通过查看文档可以知道它的实现类只有一个:ReentrantReadWriteLock(重入可读写锁!)
在这里插入图片描述
模拟一个缓存的例子:

  /**
     * 自定义一个缓存!
     */
    class Mychche {
        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() + "写入OK");
        }

        //取 读; 这里可以有多个线程读!
        public void get(String key) {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        }
    }

主启动类代码:

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        Mychche mychche = new Mychche();
        //写入
        for (int i = 1; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                mychche.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
            //读取
            //写入
            for (int j = 1; j < 5; j++) {
                final int t = j;
                new Thread(() -> {
                    mychche.get(t+"");
                }, String.valueOf(j)).start();
            }
        }
    }
}

通过运行上面的代码;会产生下面的结果(结果不唯一)
在这里插入图片描述
从上面的结果不难看出,这种方法是存在问题的,比如2的写入应该是在2的读取OK之后,同时写入之间可能会发生插入,比如:写入1,但是1还没写入ok的时候又写入2等等问题!
为了解决上面的问题;就可以使用读写锁!改变的代码如下:

/**
 * 自定义一个缓存!
 */
class MychcheLock {
    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁:更加细粒度的控制
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    //存 写;这里希望只有一个线程可以存/写
    public void put(String key, Object value) {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }

    //取 读; 这里希望可以有多个线程读!
    public void get(String key) {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }
}

在这里插入图片描述
可以看到写的时候只有一个线程写!但是读取的时候可以有多个线程读取!
在这里插入图片描述
注意:读写锁其中的写锁,又叫做独占锁:一次只能被一个线程占有;读锁又叫做共享锁,多个线程可以同时占有!

阻塞队列(BlockQueue)

阻塞:不得不阻塞的情况有两种:在写入时以及读取时
队列:先进先出(FIFO)
在这里插入图片描述
阻塞队列:
在这里插入图片描述
在这里插入图片描述

什么情况下使用阻塞队列

多线程下,A线程要调用B线程的情况,那么B必须先执行,如果B没有执行完,那么A线程会挂起!还有就是线程池也会用到!

四组API

第一组API,会抛出异常的:一言不合就开干

添加元素:add(e):

当队列未满的时候,向队列中添加元素正常;当队列满的时候,再向队列中添加元素的话,会抛出throw new IllegalStateException(“Queue full”);异常。
代码实现:

public class Test {
    public static void main(String[] args) {
        test1();
    }

    /**
     * 抛出异常
     */
    public static void test1(){
    //参数为队列的大小;
        ArrayBlockingQueue bq = new ArrayBlockingQueue<>(3);
        System.out.println(bq.add("a"));
        System.out.println(bq.add("b"));
        System.out.println(bq.add("c"));
    }
}

当我们添加的数量等于小于队列的大小时;输出
在这里插入图片描述
但是当添加的数量大于队列时,会出现异常:无效状态异常:队列空了
在这里插入图片描述
删除元素:remove()

当队列不为空的时候,调用该方法,返回被移除的元素;当队列为空的时候在调用该方法,会抛出异常。

 System.out.println(bq.remove());
 System.out.println(bq.remove());
 System.out.println(bq.remove());

在这里插入图片描述
但是多写一个移除的话,会出现下面的异常:没有元素
在这里插入图片描述

第二组:带有返回值的,不会抛出异常:为人处事会圆滑了

添加元素:offer(e)

需要主要:这里的offer方法只有一个参数,这个和我们后面讲解的一组的区别

当队列未满的时候,向队列中添加元素,返回true;当队列已经满了,继续向队列中添加元素的话,不会抛出异常,会返回false.

  public static void test2(){
        ArrayBlockingQueue bq = new ArrayBlockingQueue<>(3);
        System.out.println(bq.offer("a"));
        System.out.println(bq.offer("b"));
        System.out.println(bq.offer("c"));
        System.out.println(bq.offer("d"));

    }

此时不会抛出异常:会返回一个布尔值
在这里插入图片描述
删除元素:poll()

注意:参数为空哦!

当队列不为空的时候,返回被移除的元素,当队列为空的时候,返回null.而不是抛出异常。

第三组:阻塞,一直等待:三十而立,咬定青山不放松

添加元素:put(e)

当队列满的时候,进入阻塞等待状态,一直等待,直到可以添加到队列中为止。

需要说明:在阻塞等待过程中,有可能会被中断,所以会抛出中断异常:throws InterruptedException。

 public static void test3() throws InterruptedException {
        ArrayBlockingQueue bq = new ArrayBlockingQueue<>(3);
        bq.put("a");
        bq.put("b");
        bq.put("c");
        bq.put("d");       
    }

可以发现不会异常,会一直等待!
在这里插入图片描述

删除元素:take()

当队列不为空的时候,返回被移除的元素;当队列为空的时候,进入阻塞等待状态。

 public static void test3() throws InterruptedException {
        ArrayBlockingQueue bq = new ArrayBlockingQueue<>(3);
        bq.put("a");
        bq.put("b");
        bq.put("c");
        System.out.println(bq.take());
        System.out.println(bq.take());
        System.out.println(bq.take());
        System.out.println(bq.take());

    }

因为这里只添加了三个,但是移除了4个,所以最后一个的时候,会一直等待:
在这里插入图片描述

第四组:带有等待超时的阻塞API

添加元素:offer(e,time,unit)

参数说明:

e:将要被添加到队列中的元素

time:long类型的。预设定的需要等待的时间

unit:TimeUnit.超时时间的单位

 public static void test4() throws InterruptedException {
        ArrayBlockingQueue bq = new ArrayBlockingQueue<>(3);
        bq.offer("a");
        bq.offer("b");
        bq.offer("c");
        bq.offer("d",2,TimeUnit.SECONDS);//前面是满的,等待超过2s就退出!
    }

删除元素:poll(time,unit)

当队列为空的时候,进入阻塞等待,等到超时时间的时候,返回null.退出等待。

除了上面的添加和删除,还有一个获取队首元素!
总结四组API如下:

方式抛出异常有返回值,不抛出异常阻塞等待超时等待
添加addoffer()put()offer(e,time,unit)
移除removepoll()take()poll(time,unit)
检测队首元素elementpeek--

同步队列(SynchronousQueue)

该队列进去一个元素,必须等待取出来之后,才能再往里面放一个元素!
出入方法:put
移除方法:take
同步队列

  • 和其他的BlockingQueue 不一样 SynchronousQueue 不存储元素
  • put了一个元素,必须从里面先take取出来,否则不能再put进去值
 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(3);//等待3秒
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);//等待3秒
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);//等待3秒
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();

    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值