JDK并发包

一、

重入锁ReentrantLock:
重入锁可以完全代替synchronized。开发人员必须手动指定何时加锁,何时释放锁。在退出临界区时,必须记得释放锁。

重入锁重要方法:
lock():获得锁,如果锁已经被占用,则等待。
lockInterruptibly():获得锁,但优先响应中断。
tryLock():尝试获得锁,如果成功,则返回true,失败返回false。该方法不等待,立即返回。
tryLock(long time,TimeUnit unit):在给定时间内尝试获得锁。
unlock():释放锁。

如果用synchronized关键字进行锁控制,产生的锁是非公平的。
使用重入锁,当参数fair为true时,则表示锁时公平的,线程交替获得锁。

public static ReentrantLock fairLock = new ReentrantLock(true);

Condition:
通过lock接口的Condition newCondition()方法可以生成一个与当前重入锁绑定的Condition实例。利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。
await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()方法或者signalAll()方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。与Object.wait()方法相似
awaitUninterruptibly()方法与await()方法基本相同,但是它并不会在等待过程中响应中断。
signal()方法用于唤醒一个在等待中的线程,signalAll()方法会唤醒所有在等待中的线程。与Object,notify()方法相似

允许多个线程同时访问:信号量(Semaphore)
无论是内部锁synchronized还是重入锁reentrantlock,一次都只允许一个线程访问资源二信号量却可以指定多个线程同时访问某一个资源。
在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。

ReadWriteLock读写锁
读写锁允许多个线程同时读,使得同时读的线程真正并行。

非阻塞阻塞
阻塞阻塞

倒计数器CountDownLatch
这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计数结束再开始执行。

循环栅栏:CyclicBarrier
CyclicBarrier时另外一种多线程并发控制工具。和CutDownLatch非常类似,它也可以实现线程间的计数等待,但他的功能比CutDownLatch更加复杂且强大。比如,我们将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,接着凑齐下一批10个线程,这就是循环栅栏的内在的含义。

线程阻塞工具类:LockSupport
它可以在线程内任意位置让线程阻塞。与Thread.suspend()方法相比,它弥补了由于resume()方法发生导致线程无法继续执行的情况。与Object.wait()方法相比,它既不需要先获得某个对象的锁,也不会抛出InterruptedException异常。
LockSupport的静态方法park()可以阻塞当前线程,类似的还有parkNanos()parkUntil()等方法。他们实现了一个限时的等待。

不管unpark()方法在park()方法之前还是之后,都能正常结束。因为LockSupport类使用类似信号量的机制。它为每一个线程准备了一个许可,如果许可可用,那么park()方法会立即返回,并且消费这个许可(也就是将许可变为不可用),如果许可不可用就会阻塞,而unpark()方法则使得一个许可变为可用(但是和信号量不同的是,许可不能累加,你不能拥有超过一个许可,它永远只有一个)。
这个特点使得:即使unpark()方法操作发生在park()方法之前,它也可以使下一次park()方法操作立即返回。
LockSupport.park()方法还能支持中断影响。但是和其他接收中断的函数很不一样,LockSupport.park()方法不会抛出InterruptedException异常。它会默默返回,但是我们可以从Thread.interred()等方法中获得中断标记。

Guava和RateLimiter限流:

漏桶算法的基本思想是:利用一个缓存区,当有请求进入系统时,无论请求的速率如何,都先在缓存区内保存,然后以固定的流速流出缓存区进行处理。

令牌算法是一种反向的漏桶算法。在令牌桶算法中,桶中存放的不再是请求,而是令牌。程序处理只有拿到令牌后,才能对请求进行处理。如果没有令牌,那么处理程序要么丢弃请求,要么等待可用的令牌。为了限制流速,该算法在每个单位时间产生一定量的令牌存入桶中。

二、线程池

在线程池中,总有那么几个活跃线程。当你需要使用线程时,可以从池子中随便拿一个空闲线程,当完成工作时,并不急着关闭线程,而是将这个线程退回到线程池中。

计划任务
对于FixedRate方式来说,任务调度的频率是一定的。它是以上一个任务开始执行时间为起点,在之后的 period时间调度下一次任务。而FixDelay方式则是在上一个任务结束后,再经过delay时间进行任务调度。

注意:如果任务遇到异常,那么后续的所有子任务都会停止调度,因此,必须保证异常被及时处理,为周期性 任务的稳定调度提供条件。

有界的任务队列:当使用有界的任务队列时,若有新的任务需要执行,如果线程池的实际线程数小于corePoolSize,则会优 先创建新的线程,若大于corePoolSize,则会将新任务加入等待队列。若等待队列已满,无法加入,则在 总线程数不大于maximumPoolSize的前提下,创建新的进程执行任务。若大于maximumPoolSize,则执 行拒绝策略。

无界的任务队列:与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务 到来,系统的线程数小于corePoolSize时,线程池会生成新的线程执行任务,但当系统的线程数达到 corePoolSize后,就不会继续增加了。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接 进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

优先任务队列:优先任务队列是带有执行优先级的队列。它通过PriorityBlockingQueue类实现,可以控制任务的执行先 后顺序。它是一个特殊的无界队列。

拒绝策略
JDK内置的拒绝策略如下:
● AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
● CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然 这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
● DiscardOldestPolicy策 略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
● DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,我觉得这 可能是最好的一种方案了吧!

三、JDK的并发容器


● ConcurrentHashMap:这是一个高效的并发HashMap。你可以把它理解为一个线程安全的 HashMap。
● CopyOnWriteArrayList:这是一个List,从名字看就知道它和ArrayList是一族的。在读多 写少的场合,这个List的性能非常好,远远优于Vector。
● ConcurrentLinkedQueue:高效的并发队列, 使用链表实现。可以看作一个线程安全的LinkedList。
● BlockingQueue:这是一个接口,JDK内部通过 链表、数组等方式实现了这个接口。表示阻塞队列,非常适合作为数据共享的通道。
● ConcurrentSkipListMap:跳表的实现。这是一个Map,使用跳表的数据结构进行快速查找。

CopyOnWriteArrayList
为了将读取的性能发挥到极致,JDK中提供了CopyOnWriteArrayList类。对它来说,读取是完全不用加 锁的,并且更好的消息是:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。

在写入操作时,进行一次自我复制。换句话说,当这个List需要修改时,我并不修改原有的内容(这对 于保证当前在读线程的数据一致性非常重要),而是对原有的数据进行一次复制,将修改的内容写入副 本中。写完之后,再用修改完的副本替换原来的数据。

BlockingQueue
我们既希望线程A能够通知线程B,又希望线程A不知道线程B的存在。这样,如果将来进行重构或者升 级,我们完全可以不修改线程A,而直接把线程B升级为线程C,保证系统的平滑过渡。而这中间的“意 见箱”就可以使用BlockingQueue来实现。

从队列中弹出元素可以使用poll()方法和take()方法。它们都从队列的头部获得一个元素。不同之处在 于:如果队列为空,那么poll()方法会直接返回null,而take()方法会等待,直到队列内有可用元素。

跳表(SkipList)
跳表是一种可以用来快速查找的数据结构
跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦发现被查找的元素大于当 前链表中的取值,就会转入下一层链表继续找。

CopyOnWriteArrayList类与ConcurrentLinkedQueue类
在绝大部分场景中,CopyOnWriteArrayList类要优于ConcurrentLinkedQueue类。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值