JUC——基础

本文详细介绍了Java并发编程中的重要组件,包括volatile关键字、CAS算法、线程安全的集合、CountDownLatch、CyclicBarrier、Semaphore、读写锁、阻塞队列以及线程池的工作原理和配置策略,如四大拒绝策略。还讨论了ForkJoinPool在特定场景下的高效应用。
摘要由CSDN通过智能技术生成

juc 与 jvm - Java 必学 2019版本-阳哥- 尚硅谷- idea

volatile关键字

volatile关键字相较于synchronized是一种较为轻量级的同步锁。

  1. 不具备“互斥性”
  2. 不保证变量的原子性

CAS算法

  • CAS算法是硬件对于并发操作共享数据的支持
  • CAS包含了三个操作数:
    内存值V、预估值A、更新值B
  • 当且仅当V==A时,V<==B;否则,将不做任何操作。

list:集合与线程安全

下面代码可能会java.util.ConcurrentModificationException错误

//可能会报java.util.ConcurrentModificationException
    @Test
    public void testArrayList(){
        List<String> list = new ArrayList<>();

        for (int i = 0; i < 80; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }

改进方法:

  1. Vector
  2. List<String> list = Collections.synchronizedList(new ArrayList<>());
  3. CopyOnWriteArrayList:写入时复制
    链接:CopyOnWriteArrayList详解
  4. 同理,set有对应的2和3
  5. map有对应的2和ConcurrentHashMap
public boolean add(E e) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            //复制到一个长度加1的新数组
            Object[] newElements = Arrays.copyOf(elements, len + 1); 
            newElements[len] = e;
            setArray(newElements);
            return true;
        }
    }

CountDownLatch

属于包:import java.util.concurrent.CountDownLatch;
CountDownLatch的静态内部类Sync,是AbstractQueuedSynchronizer的子类

demo1:

CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "\t离开教室");
                countDownLatch.countDown();
            },String.valueOf(i+1)).start();

        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t 关门走人");

结果:

3	离开教室
4	离开教室
5	离开教室
2	离开教室
6	离开教室
1	离开教室
main	 关门走人

Process finished with exit code 0

CyclicBarrier

CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("*********召唤神龙********");
        });

        for (int i = 0; i < 7; i++) {
            int finalI = i+1;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集到"+ finalI +"颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i+1)).start();
        }

结果:

3收集到3颗龙珠
6收集到6颗龙珠
2收集到2颗龙珠
5收集到5颗龙珠
1收集到1颗龙珠
4收集到4颗龙珠
7收集到7颗龙珠
*********召唤神龙********

Process finished with exit code 0

Semaphore

  • new Semaphore(3)定义信号量的数目
  • acquire:成功获取信号量(信号量-1)
  • release:成功释放信号量(信号量+1)
 public static void main(String[] args) {
        //模拟被抢夺的资源
        final Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "\t 抢到了semaphore");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + "\t 释放了semaphore");
                }
                },String.valueOf(i+1)).start();
        }
    }

读写锁:ReadWriteLockTest

  • 读锁:readWriteLock.readLock() 返回静态内部类ReadLock,ReadLock实现了Lock接口
  • 写锁:readWriteLock.writeLock() 返回静态内部类WriteLock,WriteLock实现了Lock接口
public class ReadWriteLockTest {
    public static void main(String[] args) {
        final Library library = new Library();
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                library.put(finalI +" ",new Book(String.valueOf(finalI+1001)));
            },String.valueOf(i+1)).start();
        }
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                library.get(finalI +" ");
            },String.valueOf(i+1)).start();
        }
    }
}
class Library{
    private volatile Map<String,Book> map = new HashMap<>();
    ReentrantReadWriteLock readWriteLock= new ReentrantReadWriteLock();

    public void put(String key, Book value){
        readWriteLock.writeLock().lock();
        System.out.println(Thread.currentThread().getName()+"\t 写入\t"+key+"中……");
        try {
            Thread.sleep(300);

            map.put(key,value);
            System.out.println("写入完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            readWriteLock.writeLock().unlock();
        }
    }
    public Book get(String key){
        readWriteLock.readLock().lock();
        System.out.println(Thread.currentThread().getName()+"\t 读取中……");
        Book book = null;
        try {
            Thread.sleep(50);
            book = map.get(key);
            System.out.println("读取完成"+book);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            readWriteLock.readLock().unlock();
        }
        return book;
    }
}
class Book{
    String desc;

    public Book(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Book{" +
                "desc='" + desc + '\'' +
                '}';
    }
}

BlockingQueue阻塞队列

ArrayBlockingQueue

  • 构造器:没有无参构造器
    • new ArrayBlockingQueue<>(3);
  • add(e):
    • 当阻塞队列满时,抛出java.lang.IllegalStateException: Queue full异常
  • remove():
    • 当阻塞队列空时,抛出java.util.NoSuchElementException异常
  • offer(e):
    • 当阻塞队列满时,返回false
  • poll():
    • 当阻塞队列空时,返回false
  • put(e)
    • 当阻塞队列满时,阻塞直到put数据或者响应中断退出
  • take()
    • 当阻塞队列空时,阻塞知道队列可用

LinkedBlockingQueue

由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。

SynchronousQueue

不存储元素的阻塞队列,也即单个元素的队列

ThreadPool线程池

线程池主要参数

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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • 七大参数
    • corePoolSize
    • maximumPoolSize
    • workQueue :任务队列,被提交但未执行的任务
    • keepAliveTime
    • unit
    • threadFactory :表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认的即可。
    • handler :拒绝策略。当队列满了,并且工作线程数大于等于线程池的最大线程数时如何来拒绝请求执行的Runnable的策略

处理流程:

  • 核心线程池是否已满:
    • 若否,则创建线程执行任务;
    • 若是,则进行下一步判断。
  • 队列是否已满:
    • 若否,则将任务储存在队列里;
    • 若是,则进行下一步判断。
  • 线程池是否已满:
    • 若否,则创建线程执行任务;
    • 若是,则按照策略处理无法执行的任务。

3种常用线程池:

public static ExecutorService newCachedThreadPool() {
    new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
         (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

* 不建议使用Executors创建线程池

《阿里巴巴java开发手册》:

【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1) FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
2) CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

4种拒绝策略

参考线程池4种拒绝策略和5种状态

AbortPolicy(默认)

将抛出 RejectedExecutionException.

public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
CallerRunsPolicy

用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。

public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
DiscardOldestPolicy

当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }
DiscardPolicy

当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         * 什么也不做
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

ForkJoinPool

ForkJoinPool 不是为了替代 ExecutorService,而是它的补充,在某些应用场景下性能比 ExecutorService 更好。
ForkJoinPool 主要用于实现“分而治之”的算法,特别是分治之后递归调用的函数,例如 quick sort 等。
ForkJoinPool 最适合的是计算密集型的任务,如果存在 I/O,线程间同步,sleep() 等会造成线程长时间阻塞的情况时,最好配合使用 ManagedBlocker。
介绍 ForkJoinPool 的适用场景,实现原理

想跑步丶小胖子 2019-06-17 16:05:53 13362 收藏 18
分类专栏: Java
原文:https://blog.csdn.net/f641385712/article/details/83749798

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值