本文主要内容:读写锁、阻塞队列和自定义线程池

五、读写锁

ReentrantReadWriteLock:读写锁出现就是为了更加方便的操作多条线程对资源的读写

readWriteLock.readLock():读锁 == 共享锁

readWriteLock.writeLock():写锁 == 独占锁

public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyThreads myThreads = new MyThreads();

        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myThreads.write(temp + "", temp);
            }, String.valueOf(i)).start();
        }

        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> myThreads.read(temp + ""), String.valueOf(i)).start();
        }

    }
}
class MyThreads{
    private volatile Map<String, Object> map = new HashMap<>();

    // 声明一个读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 写入的时候只有一个线程可以写,不希望被其它线程插入(就是不希望在A线程正在写入和写入成功成功之间有其它线程进入)
    public void write(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) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    // 读取的时候多条线程可以同时读取
    public void read(String key){
        readWriteLock.readLock().lock();
        try {
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

六、阻塞队列

public class BlockingQueueTest {

    /**
     * 阻塞队列API一:add()与remove()方法
     */
    @Test
    public void test1(){
        // 声明一个队列长度为3的阻塞队列
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);

        // 添加元素
        System.out.println(queue.add("a"));
        System.out.println(queue.add("b"));
        System.out.println(queue.add("c"));

        // 如果此时继续使用add方法会抛出异常(超过队列长度) java.lang.IllegalStateException: Queue full
        // System.out.println(queue.add("d"));

        // 获取元素
        System.out.println(queue.remove());
        System.out.println(queue.remove());
        System.out.println(queue.remove());

        // 如果此时继续使用add方法会抛出异常(没有元素) java.util.NoSuchElementException
        // System.out.println(queue.remove());
    }

    /**
     * 阻塞队列API二:offer()与poll()方法
     */
    @Test
    public void test2(){
        // 声明一个队列长度为3的阻塞队列
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);

        // 添加元素
        System.out.println(queue.offer("a"));
        System.out.println(queue.offer("b"));
        System.out.println(queue.offer("c"));

        // 如果此时继续使用offer方法不会抛出异常,只会返回false
        System.out.println(queue.offer("d"));

        // 获取元素
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());

        // 如果此时继续使用offer方法不会抛出异常,只会返回null
        System.out.println(queue.poll());
    }

    /**
     * 阻塞队列API三:put()与take()方法
     */
    @Test
    public void test3() throws InterruptedException {
        // 声明一个队列长度为3的阻塞队列
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);

        // 添加元素
        queue.put("a");
        queue.put("b");
        queue.put("c");

        // 如果此时继续使用put方法不会抛出异常,只会一直阻塞
        // queue.put("d");

        // 获取元素
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());

        //如果此时继续使用take方法不会抛出异常,只会一直阻塞
        //System.out.println(queue.take());
    }

    /**
     * 阻塞队列API四:offer()与poll()方法的带参版
     */
    @Test
    public void test4() throws InterruptedException {
        // 声明一个队列长度为3的阻塞队列
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);

        // 添加元素
        System.out.println(queue.offer("a"));
        System.out.println(queue.offer("b"));
        System.out.println(queue.offer("c"));

        // 如果此时继续使用offer方法传递2s,则会在2s之后退出阻塞
        System.out.println(queue.offer("d",2, TimeUnit.SECONDS));

        // 获取元素
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());

        // 如果此时继续使用poll方法传递2s,则会在2s之后退出阻塞
        System.out.println(queue.poll(2, TimeUnit.SECONDS));
    }
}
四种情况分析报出异常不报异常一直阻塞超时退出
添加方法add()offer()put()offer(参数)
删除方法remove()poll()take()poll(参数)

七、线程池

创建线程池的三大方法

public class ThreadPool {
    public static void main(String[] args) {
        // 创建只有一个线程的线程池
        // ExecutorService executor = Executors.newSingleThreadExecutor();
        // 创建有自定义个线程的线程池
        // ExecutorService executor = Executors.newFixedThreadPool(5);
        // 创建弹性线程池,根据需要的线程数量,自行创建
        ExecutorService executor = Executors.newCachedThreadPool();

        try {
            for (int i = 1; i <= 5; i++) {
                executor.execute(() -> {
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executor.shutdown();
        }
    }
}

自定义线程池所需的七大参数和四种拒绝策略(因为我们需要规避默认创建线程池的OOM问题)

public class ThreadPool {
    public static void main(String[] args) {
        // 创建自定义线程池
        ExecutorService executor = new ThreadPoolExecutor(
                2, // 池的核心大小
                // 池的最大线程数:一般要根据需求确定是CPU密集型还是IO密集型
                // CPU密集型:池的最大线程数为CPU核心数 Runtime.getRuntime().availableProcessors() --> 获取CPU最大核心数
                // IO密集型:池的池的最大线程数一般为频繁IO操作任务的数量 * 2
                Runtime.getRuntime().availableProcessors(),
                2, TimeUnit.SECONDS, // 超时过期时间
                new LinkedBlockingQueue<>(3), // 阻塞队列
                Executors.defaultThreadFactory(), // 创建线程工厂
                // 拒绝策略1.new ThreadPoolExecutor.AbortPolicy()   线程池已经耗尽,如果还需要获取线程就直接抛出异常
                // 拒绝策略2.new ThreadPoolExecutor.CallerRunsPolicy() 线程池已经耗尽,如果还需要获取线程,就直接让其回到创建线程的线程去执行(主线程)
                // 拒绝策略3.new ThreadPoolExecutor.DiscardPolicy() 线程池已经耗尽,如果还需要获取线程,就不去处理该方法即不抛出异常
                // 拒绝策略4.new ThreadPoolExecutor.DiscardOldestPolicy() 线程池已经耗尽,如果还需要获取线程,就尝试和线程池最老线程争取资源
                // 线程池的最大承载量为:池的最大线程数 + 阻塞队列可以容纳的容量
                new ThreadPoolExecutor.AbortPolicy()
        );

        try {
            for (int i = 1; i <= 8; i++) {
                executor.execute(() -> {
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executor.shutdown();
        }
    }
}

本文由 WarlockMT 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。