【狂神说java juc 20200219 03篇】读写锁,阻塞 同步队列,线程池,四大函数式接口

1. 读写锁 ReentrantReadWriteLock

  • java.util.concurrent
    • Condition
    • Lock
    • ReadWriteLock
      • ReentrantReadWriteLock

A ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。 read lock可以由多个阅读器线程同时进行,只要没有作者。 write lock是独家的。

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * ReadWriteLock
 * 读-读  可以共存!
 * 读-写  不能共存!
 * 写-写  不能共存!
 */
public class ReadWriteLockDemo {
    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 MyCacheLock{

    private volatile Map<String,Object> map = new HashMap<>();
    // 读写锁: 更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //private Lock lock = new ReentrantLock();

    // 存,写入的时候,只希望同时只有一个线程写
    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()+"写入OK");
        } 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()+"读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }

}
Reentrant
英
/riːˈentrənt/
adj.
再进去的;凹角的
n.
凹角;再进入

2. 阻塞队列 BlockingDeque

  • 写入:如果队列满了,就必须阻塞等待
  • 读取:如果队列是空的,必须阻塞等待生产

结构

Collection

  • Queue
    • Abstract Queue 非阻塞队列
      • Concurrent Linked Queue
    • Blocking Queue 阻塞队列
      • Array Blocking Queue
      • Linked Blocking Queue
      • Synchron ous Queue
    • Deque 双端队列
      • Blocking Deque,继承上面两个
  • Set
  • List
    • ArrayList
    • LinkedList

四组API

  • 为何使用

A —> B,如果B未执行完,A要等待。

多线程并发处理,线程池!

方式抛出异常有返回值,不抛出异常阻塞等待超时等待
添加addoffer()put()offer(,)
移除removepoll()take()poll(,)
检测队首元素elementpeek--
抛出异常
        // 队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(1);

        System.out.println(blockingQueue.add("a"));
 
        // IllegalStateException: Queue full 抛出异常!
        // System.out.println(blockingQueue.add("d"));

        System.out.println("=-===========");

        System.out.println(blockingQueue.element()); // 查看队首元素是谁,如果已经移出去 第一个,那就是第二个
        System.out.println(blockingQueue.remove());

        // java.util.NoSuchElementException 抛出异常!
        // System.out.println(blockingQueue.remove());
不抛出异常
        // 队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("a"));

        System.out.println(blockingQueue.peek()); //获取首元素
        // System.out.println(blockingQueue.offer("d")); // false 不抛出异常!
        System.out.println("============================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll()); //没了,返回 null  不抛出异常!
peek
v.
偷看,窥视;微露出,探出
n.
一瞥,偷偷地一看;(计算机)读取数据

offer
v.
提供,给予;提议,表示愿意(做某事);出(价),开(价);提出,作出;表示(爱、友谊等);(向上帝或神)奉献(祈祷、赞美),祭献(牺牲); 试图(使用暴力,进行抵抗);<古>发动,挑起(战争);把……摆好(或放到位)(以便检测或评估其外观或状况);<旧>(机会)出现;求婚

poll
英
/pəʊl/
n.
民意调查,民意测验;选举投票,计票;投票数;投票点(the polls);头顶,头皮;无角动物(尤指无角牛)
v.
对……进行民意测验(调查);获得(票数);(电信,计算机)轮询,探询;截掉(动物,尤指小牛)的角;<古>修枝
* 阻塞等待
/**
     * 等待,阻塞(一直阻塞)
     */
    public static void test3() throws InterruptedException {
        // 队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(1);

        // 一直阻塞
        blockingQueue.put("a");

        // blockingQueue.put("d"); // 队列没有位置了,一直阻塞
        System.out.println(blockingQueue.take());

        System.out.println(blockingQueue.take()); // 没有这个元素,一直阻塞

    }
  • 使用案例
public class MyTest {

    public static void main(String[] args) {
        LinkedBlockingDeque<Integer> queue = new LinkedBlockingDeque<>(1);

        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
        
        System.out.println("主线程执行顺序不确定");
    }
}


class Producer implements Runnable {

    protected LinkedBlockingDeque<Integer> queue = null;

    public Producer(LinkedBlockingDeque queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(3000);
            System.out.println("2. 进行添加了");
            //这里对应错误了,应该是 put 对应take
            queue.add(1);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {

    protected LinkedBlockingDeque<Integer> queue = null;

    public Consumer(LinkedBlockingDeque queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            //队列为空,必须阻塞 等着添加
            System.out.println("3. 我一定是在 添加后面执行,添加睡几秒,我就等待它 " + queue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
超时等待
    /**
     * 等待,阻塞(等待超时)
     */
    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); // 等待超过2秒就退出,不报错
        
        System.out.println("===============");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        blockingQueue.poll(2, TimeUnit.SECONDS); // 等待超过2秒就退出

    }

SynchronousQueue 同步队列

  • 阻塞队列下的

  • 没有容量,

  • 进去一个元素,必须等待取出来之后,才能再往里面放一个元素!

  • put、take

/**
 * 同步队列
 * 和其他的BlockingQueue 不一样, SynchronousQueue 不存储元素
 * put了一个元素,必须从里面先take取出来,否则不能在put进去值!
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); // 同步队列

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(6);
                System.out.println(Thread.currentThread().getName() + " put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " put 2");
                blockingQueue.put("2");

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


        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=>" + blockingQueue.take());

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();
    }
}
queue
英
/kjuː/
n.
<英>(人、汽车等的)队,行列;<英>(为得到某机会而等待的)长列,长队;(计算机)队列;呼叫队列;<古>辫子
v.
<英>排队(等候);竞相,抢着(做某事);(计算机)排成队列,排队

synchronous
英
/ˈsɪŋkrənəs/
同步的

3. 线程池

线程池:三大方法、7大参数、4种拒绝策略

池化技术

程序的运行,本质:占用系统的资源!

优化资源的使用!=>池化技术

线程池、连接池、内存池、对象池,常量池///… 创建、销毁。十分浪费资源

池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

线程池的好处**😗*

1、降低资源的消耗

2、提高响应的速度

3、方便管理。

线程复用、可以控制最大并发数、管理线程

线程池 不允许使用 Executors 创建,而是通过 ThreadPoolExecutor

  • 更贱明确 线程的运行规则,避免资源耗尽的风险
  • 允许 Integer.MAX_VALUE个线程,可能报 OOM 内存溢出

3大方法

        //单一
        Executors.new Single Thread Executor();
        //固定的值
        Executors.new Fixed ThreadPool(5);
        //缓存,可伸缩的,遇强则强
        Executors.new Cached ThreadPool();

//还有 调度行 创建,参数为 核心线程数
Executors.newScheduledThreadPool(5);
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        try {
            for (int i = 0; i < 100; i++) { // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally { // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }

scheduled
英
/ˈʃedjuːld/
adj.
预先安排的,按时刻表的;(尤指航班)定期的;<英>(建筑,历史遗迹)列入文物保护单位的
v.
安排;将……列入计划表(或时间表等);<英>把(建筑物)列为文物保护单位(schedule 的过去式和过去分词形式)

7大参数

  • 正常 1,2 号口,可办业务。 核心线程池大小
    • 其他人,去 候客区等。 1 2 3。 候客区就是 阻塞队列
  • 人特别多,候客区满了,在开 3,4,5窗口。最大线程数
    • 假如 再开的 这三个窗口,也满了。又来了人,就要拒绝策略了。
    • 3,4,5 窗口,1个小时都没有业务。关闭释放。超时不候
    • 队列 ,候客区,这里只能放3个人。 候客区
    • 线程工厂,可以用默认的
  1. 核心线程数
  2. 最大线程数
  3. 多少单位 超时没有业务,关闭释放 非核心的线程。如上 3,4,5 。1个小时都没任务,就释放。
  4. 超时的单位
  5. 队列。候客区
  6. 线程工厂
  7. 拒绝策略
源码 Executors
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

	//所以 不要用这个创建
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //最大为21亿,会oom
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

//本质 ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
                              int maximumPoolSize, // 最大核心线程池大小
                              long keepAliveTime,// 超时了没有人调用就会释放
                              TimeUnit unit, // 超时单位
                              BlockingQueue<Runnable> workQueue, // 阻塞队列
                              ThreadFactory threadFactory,// 线程工厂:创建线程的,一般 不用动
                              RejectedExecutionHandler handler) { // 拒绝策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
4种拒绝策略
/**
 * new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常
 * new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!调用的线程执行
 * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
 * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常! 一般都会竞争成功
 */
Abort
英
/əˈbɔːt/
v.
(使)流产,堕(胎);(由于问题或故障)中止,使夭折;(胚胎器官,有机体)发育不全,败育
n.
<非正式>(飞行、航天任务或其他事业的)中断,取消;夭折的计划(或事业)


Policy
英
/ˈpɒləsi/
n.
政策,方针;(处事) 原则,策略;保险单

discard
v.
扔掉,弃置;打出(无用的牌),垫(牌)
n.
被抛弃物;(纸牌游戏中)垫出的牌
案例 ThreadPoolExecutor
public class Demo01 {
    public static void main(String[] args) {
        // 自定义线程池!工作 ThreadPoolExecutor

        // 最大线程到底该如何定义:
        // 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!
        // 2、IO  密集型   > 判断你程序中十分耗IO的线程。比如设置2倍
        // 程序   15个大型任务  io十分占用资源!

        // 获取CPU的核数,比如6核心12线程,即是12
        //System.out.println(Runtime.getRuntime().availableProcessors());

        List  list = new ArrayList();
		//核心 2个,上个例子 最大5个,3秒超时,就不等了。
        //阻塞Linked(等候区有三个,最多最多5+3=8个,超过8个就走拒绝策略),
        //和 默认的工厂。拒绝策略
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(), //我的12核心,这参数大概能执行24个,不确定,最好能执行29个
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());  //队列满了,尝试去和最早的竞争,也不会抛出异常!
        try {
            // 最大承载:Deque + max
            // 超过 RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" ok");
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }

    }
}

4. 四大函数式接口

新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算

函数式接口:

只有一个方法的接口(但 可以有默认方法 和 静态方法)

  • 在包:java.util.function
    • Consumer
      • 两个参数 没有返回值 的是:BiConsumer
      • IntConsumer 是有一个int的参数
    • Funtion
    • Predicate
    • Supplier 无参数,有返回值。
      • 如果 无参数,无返回值的 那就是 Runnable
@FunctionalInterface 
public interface Runnable { 
	public abstract void run(); 
}

// 泛型、枚举、反射 
// lambda表达式、链式编程、函数式接口、Stream流式计算 
// 超级多FunctionalInterface 
// 简化编程模型,在新版本的框架底层大量应用! 
// foreach(消费者类的函数式接口)

集合的 forEach

Iterable
	
    //消费者 类型的 函数式 接口
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

Function

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);  //参数T,返回R
}
        Function<String,String> function1 = new Function<String,String>() {
            @Override
            public String apply(String str) {
                return str;
            }
        };
		//简化1
        Function<String, String> function2 = (String str) -> {
            return str;
        };
		//简化2
        Function<String,String> function = str->{return str;};

		//简化3
        Function<String, String> function2 = str -> str;

        System.out.println(function.apply("asd"));

Predicate

predicate
n.
(语法)谓语;(逻)谓项
v.
使基于,使取决于;表明,断言
adj.
(用作)表语的
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

断定型接口:有一个输入参数,返回值只能是 布尔值!

        String str=" ";
        System.out.println(str.isEmpty()); // 空格都是为false。但是:null报空指针  
        // 判断字符串是否为空
        Predicate<String> predicate1 = new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return str.isEmpty();
            }
        };

        //最简化
        Predicate<String> predicate2 = str -> str.isEmpty();

        Predicate<String> predicate = (str) -> {
            return str.isEmpty();
        };
        System.out.println(predicate2.test(""));

Consumer

Consumer 消费型接口: 只有输入,没有返回值

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

	//这个默认方法不算
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String str) {
//                System.out.println(str);
//            }
//        };

        Consumer<String> consumer = (str)->{System.out.println(str);};
        consumer.accept("sdadasd");
        
        //在简化
        Consumer<String> consumer2 = str -> System.out.println(str);
        
                new ArrayList<>().forEach(System.out::println);

Supplier

  • 供给者,提供者
英
/səˈplaɪə(r)/
美
/səˈplaɪər/

n.
供应商,供应者
@FunctionalInterface
public interface Supplier<T> {

    T get();
}

Supplier 供给型接口 没有参数,只有返回值

    public static void main(String[] args) {
//        Supplier supplier = new Supplier<Integer>() {
//            @Override
//            public Integer get() {
//                System.out.println("get()");
//                return 1024;
//            }
//        };

        Supplier supplier = ()->{ return 1024; };
        System.out.println(supplier.get());
        
        Supplier supplier = () -> 1024;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值