首页 > 编程知识 正文

java并发包源码剖析,Java并发实现原理JDK源码剖析

时间:2023-12-27 22:26:10 阅读:323792 作者:TSND

本文目录一览:

Java并行编程有什么书推荐?

《Java并发编程的艺术》(方腾飞)电子书网盘下载免费在线阅读

资源链接:

链接:

提取码: 37t6

书名:Java并发编程的艺术

作者:方腾飞

豆瓣评分:7.4

出版社:机械工业出版社

出版年份:2015-7-1

页数:240

内容简介:

并发编程领域的扛鼎之作,作者是阿里和1号店的资深Java技术专家,对并发编程有非常深入的研究,《Java并发编程的艺术》是他们多年一线开发经验的结晶。本书的部分内容在出版早期发表在Java并发编程网和InfoQ等技术社区,得到了非常高的评价。它选取了Java并发编程中最核心的技术进行讲解,从JDK源码、JVM、CPU等多角度全面剖析和讲解了Java并发编程的框架、工具、原理和方法,对Java并发编程进行了最为深入和透彻的阐述。

《Java并发编程的艺术》内容涵盖Java并发编程机制的底层实现原理、Java内存模型、Java并发编程基础、Java中的锁、并发容器和框架、原子类、并发工具类、线程池、Executor框架等主题,每个主题都做了深入的讲解,同时通过实例介绍了如何应用这些技术。

作者简介:

方腾飞(花名清英,英文名kiral),

蚂蚁金服集团技术专家,从事Java开发近10年。5年以上的团队管理、项目管理和敏捷开发经验,崇尚团队合作。曾参与CMS、电子海图、SOC、ITIL、电子商务网站和信贷管理系统等项目。目前在蚂蚁金服网商银行贷款管理团队负责数据采集平台开发工作。与同事合作开发了tala code Review插件,深受阿里数千名工程师拥趸,并开发过开源工具jdbcutil()。创办了并发编程网,组织翻译了百余篇国外优秀技术文章,并曾为InfoQ撰写“聊聊并发”专栏,在《程序员》杂志撰写敏捷实践系列文章

魏 鹏,

阿里巴巴集团技术专家,在阿里巴巴中国网站技术部工作多年,曾担任中国网站交易平台架构师,主导了交易系统服务化工作,设计实现的数据迁移系统高效地完成了阿里巴巴中国网站交易数据到阿里巴巴集团的迁移工作。目前在阿里巴巴共享业务事业部从事Java应用容器Pandora和服务框架HSF的相关工作,其中Java应用容器Pandora是阿里巴巴中间件运行的基础,而服务框架HSF则是阿里巴巴集团实现服务化的主要解决方案,二者在阿里巴巴拥有最为广泛的使用量。个人平时喜欢阅读技术书籍,翻译一些国外优秀文档,喜欢总结、乐于分享,对Java应用容器、多线程编程以及分布式系统感兴趣。

程晓明,

1号店资深架构师,从事1号店交易平台系统的开发,技术上关注并发与NIO。因5年前遇到的一个线上故障,解决过程中对Java并发编程产生了浓厚的兴趣,从此开始了漫长的探索之旅:从底层实现机制、内存模型到Java同步。纵观我自己对Java并发的学习过程,是一个从高层到底层再到高层的一个反复迭代的过程,我估计很多读者的学习过程应该与我类似。文章多见诸《IBM developerWorks》、InfoQ和《程序员》杂志。

java并发包源码怎么读

1. 各种同步控制工具的使用

1.1 ReentrantLock

ReentrantLock感觉上是synchronized的增强版,synchronized的特点是使用简单,一切交给JVM去处理,但是功能上是比较薄弱的。在JDK1.5之前,ReentrantLock的性能要好于synchronized,由于对JVM进行了优化,现在的JDK版本中,两者性能是不相上下的。如果是简单的实现,不要刻意去使用ReentrantLock。

相比于synchronized,ReentrantLock在功能上更加丰富,它具有可重入、可中断、可限时、公平锁等特点。

首先我们通过一个例子来说明ReentrantLock最初步的用法:

package test;

import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;

@Override public void run() { for (int j = 0; j 10000000; j++)

{ lock.lock(); try

{

i++;

} finally

{ lock.unlock();

}

}

}

public static void main(String[] args) throws InterruptedException {

Test test = new Test();

Thread t1 = new Thread(test);

Thread t2 = new Thread(test);

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(i);

}

}

有两个线程都对i进行++操作,为了保证线程安全,使用了 ReentrantLock,从用法上可以看出,与 synchronized相比,ReentrantLock就稍微复杂一点。因为必须在finally中进行解锁操作,如果不在 finally解锁,有可能代码出现异常锁没被释放,而synchronized是由JVM来释放锁。

那么ReentrantLock到底有哪些优秀的特点呢?

1.1.1 可重入

单线程可以重复进入,但要重复退出

lock.lock();

lock.lock();try{

i++;

}

finally{

lock.unlock();

lock.unlock();

}

由于ReentrantLock是重入锁,所以可以反复得到相同的一把锁,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放(重入锁)。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized 块时,才释放锁。

public class Child extends Father implements Runnable{    final static Child child = new Child();//为了保证锁唯一

public static void main(String[] args) {        for (int i = 0; i 50; i++) {            new Thread(child).start();

}

}

public synchronized void doSomething() {

System.out.println("1child.doSomething()");

doAnotherThing(); // 调用自己类中其他的synchronized方法

}

private synchronized void doAnotherThing() {        super.doSomething(); // 调用父类的synchronized方法

System.out.println("3child.doAnotherThing()");

}

@Override

public void run() {

child.doSomething();

}

}class Father {    public synchronized void doSomething() {

System.out.println("2father.doSomething()");

}

}

我们可以看到一个线程进入不同的 synchronized方法,是不会释放之前得到的锁的。所以输出还是顺序输出。所以synchronized也是重入锁

输出:

1child.doSomething()

2father.doSomething()

3child.doAnotherThing()

1child.doSomething()

2father.doSomething()

3child.doAnotherThing()

1child.doSomething()

2father.doSomething()

3child.doAnotherThing()

...

1.1.2.可中断

与synchronized不同的是,ReentrantLock对中断是有响应的。中断相关知识查看[高并发Java 二] 多线程基础

普通的lock.lock()是不能响应中断的,lock.lockInterruptibly()能够响应中断。

我们模拟出一个死锁现场,然后用中断来处理死锁

package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)

{ this.lock = lock;

} @Override

public void run()

{ try

{ if (lock == 1)

{

lock1.lockInterruptibly(); try

{

Thread.sleep(500);

} catch (Exception e)

{ // TODO: handle exception

}

lock2.lockInterruptibly();

} else

{

lock2.lockInterruptibly(); try

{

Thread.sleep(500);

} catch (Exception e)

{ // TODO: handle exception

}

lock1.lockInterruptibly();

}

} catch (Exception e)

{ // TODO: handle exception

} finally

{ if (lock1.isHeldByCurrentThread())

{

lock1.unlock();

} if (lock2.isHeldByCurrentThread())

{

lock2.unlock();

}

System.out.println(Thread.currentThread().getId() + ":线程退出");

}

} public static void main(String[] args) throws InterruptedException {

Test t1 = new Test(1);

Test t2 = new Test(2);

Thread thread1 = new Thread(t1);

Thread thread2 = new Thread(t2);

thread1.start();

thread2.start();

Thread.sleep(1000); //DeadlockChecker.check();

} static class DeadlockChecker

{ private final static ThreadMXBean mbean = ManagementFactory

.getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()

{ @Override

public void run()

{ // TODO Auto-generated method stub

while (true)

{ long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)

{

ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())

{ for (int i = 0; i threadInfos.length; i++)

{ if(t.getId() == threadInfos[i].getThreadId())

{

t.interrupt();

}

}

}

} try

{

Thread.sleep(5000);

} catch (Exception e)

{ // TODO: handle exception

}

}

}

};

public static void check()

{

Thread t = new Thread(deadlockChecker);

t.setDaemon(true);

t.start();

}

}

}

上述代码有可能会发生死锁,线程1得到lock1,线程2得到lock2,然后彼此又想获得对方的锁。

我们用jstack查看运行上述代码后的情况

的确发现了一个死锁。

DeadlockChecker.check();方法用来检测死锁,然后把死锁的线程中断。中断后,线程正常退出。

1.1.3.可限时

超时不能获得锁,就返回false,不会永久等待构成死锁

使用lock.tryLock(long timeout, TimeUnit unit)来实现可限时锁,参数为时间和单位。

举个例子来说明下可限时:

package test;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); @Override

public void run()

{ try

{ if (lock.tryLock(5, TimeUnit.SECONDS))

{

Thread.sleep(6000);

} else

{

System.out.println("get lock failed");

}

} catch (Exception e)

{

} finally

{ if (lock.isHeldByCurrentThread())

{

lock.unlock();

}

}

}

public static void main(String[] args)

{

Test t = new Test();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.start();

t2.start();

}

}

使用两个线程来争夺一把锁,当某个线程获得锁后,sleep6秒,每个线程都只尝试5秒去获得锁。

所以必定有一个线程无法获得锁。无法获得后就直接退出了。

输出:

get lock failed

1.1.4.公平锁

使用方式:

public ReentrantLock(boolean fair) public static ReentrantLock fairLock = new ReentrantLock(true);

一般意义上的锁是不公平的,不一定先来的线程能先得到锁,后来的线程就后得到锁。不公平的锁可能会产生饥饿现象。

公平锁的意思就是,这个锁能保证线程是先来的先得到锁。虽然公平锁不会产生饥饿现象,但是公平锁的性能会比非公平锁差很多。

1.2 Condition

Condition与ReentrantLock的关系就类似于synchronized与Object.wait()/signal()

await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()时或者signalAll()方法时,线 程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和Object.wait()方法很相似。

awaitUninterruptibly()方法与await()方法基本相同,但是它并不会再等待过程中响应中断。 singal()方法用于唤醒一个在等待中的线程。相对的singalAll()方法会唤醒所有在等待中的线程。这和Obejct.notify()方法很类似。

这里就不再详细介绍了。举个例子来说明:

package test;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static Condition condition = lock.newCondition();

@Override public void run() { try

{ lock.lock();

condition.await();

System.out.println("Thread is going on");

} catch (Exception e)

{

e.printStackTrace();

} finally

{ lock.unlock();

}

}

public static void main(String[] args) throws InterruptedException {

Test t = new Test();

Thread thread = new Thread(t);

thread.start();

Thread.sleep(2000);

lock.lock();

condition.signal(); lock.unlock();

}

}

上述例子很简单,让一个线程await住,让主线程去唤醒它。condition.await()/signal只能在得到锁以后使用。

1.3.Semaphore

对于锁来说,它是互斥的排他的。意思就是,只要我获得了锁,没人能再获得了。

而对于Semaphore来说,它允许多个线程同时进入临界区。可以认为它是一个共享锁,但是共享的额度是有限制的,额度用完了,其他没有拿到额度的线程还是要阻塞在临界区外。当额度为1时,就相等于lock

下面举个例子:

package test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class Test implements Runnable{ final Semaphore semaphore = new Semaphore(5); @Override

public void run()

{ try

{

semaphore.acquire();

Thread.sleep(2000);

System.out.println(Thread.currentThread().getId() + " done");

} catch (Exception e)

{

e.printStackTrace();

}finally {

semaphore.release();

}

}

public static void main(String[] args) throws InterruptedException {

ExecutorService executorService = Executors.newFixedThreadPool(20); final Test t = new Test(); for (int i = 0; i 20; i++)

{

executorService.submit(t);

}

}

}

有一个20个线程的线程池,每个线程都去 Semaphore的许可,Semaphore的许可只有5个,运行后可以看到,5个一批,一批一批地输出。

当然一个线程也可以一次申请多个许可

public void acquire(int permits) throws InterruptedException

1.4 ReadWriteLock

ReadWriteLock是区分功能的锁。读和写是两种不同的功能,读-读不互斥,读-写互斥,写-写互斥。

这样的设计是并发量提高了,又保证了数据安全。

使用方式:

private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();

private static Lock readLock = readWriteLock.readLock();

private static Lock writeLock = readWriteLock.writeLock();

详细例子可以查看 Java实现生产者消费者问题与读者写者问题,这里就不展开了。

1.5 CountDownLatch

倒数计时器

一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。 只有等所有检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使得点火线程

,等待所有检查线程全部完工后,再执行

使用方式:

static final CountDownLatch end = new CountDownLatch(10);

end.countDown();

end.await();

示意图:

一个简单的例子:

package test;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test implements Runnable{ static final CountDownLatch countDownLatch = new CountDownLatch(10); static final Test t = new Test(); @Override

public void run()

{ try

{

Thread.sleep(2000);

System.out.println("complete");

countDownLatch.countDown();

} catch (Exception e)

{

e.printStackTrace();

}

}

public static void main(String[] args) throws InterruptedException {

ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i 10; i++)

{

executorService.execute(t);

}

countDownLatch.await();

System.out.println("end");

executorService.shutdown();

}

}

主线程必须等待10个线程全部执行完才会输出"end"。

1.6 CyclicBarrier

和CountDownLatch相似,也是等待某些线程都做完以后再执行。与CountDownLatch区别在于这个计数器可以反复使用。比如,假设我们将计数器设置为10。那么凑齐第一批1 0个线程后,计数器就会归零,然后接着凑齐下一批10个线程

使用方式:

public CyclicBarrier(int parties, Runnable barrierAction) barrierAction就是当计数器一次计数完成后,系统会执行的动作await()

示意图:

下面举个例子:

package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)

{ this.soldier = soldier; this.cyclic = cyclic;

} @Override

public void run()

{ try

{ //等待所有士兵到齐

cyclic.await();

dowork(); //等待所有士兵完成工作

cyclic.await();

} catch (Exception e)

{ // TODO Auto-generated catch block

e.printStackTrace();

}

} private void dowork()

{ // TODO Auto-generated method stub

try

{

Thread.sleep(3000);

} catch (Exception e)

{ // TODO: handle exception

}

System.out.println(soldier + ": done");

} public static class BarrierRun implements Runnable

{ boolean flag; int n; public BarrierRun(boolean flag, int n)

{ super(); this.flag = flag; this.n = n;

} @Override

public void run()

{ if (flag)

{

System.out.println(n + "个任务完成");

} else

{

System.out.println(n + "个集合完成");

flag = true;

}

}

} public static void main(String[] args)

{ final int n = 10;

Thread[] threads = new Thread[n]; boolean flag = false;

CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));

System.out.println("集合"); for (int i = 0; i n; i++)

{

System.out.println(i + "报道");

threads[i] = new Thread(new Test("士兵" + i, barrier));

threads[i].start();

}

}

}

打印结果:

集合

士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10个任务完成

1.7 LockSupport

提供线程阻塞原语

和suspend类似

LockSupport.park();

LockSupport.unpark(t1);

与suspend相比 不容易引起线程冻结

LockSupport的思想呢,和 Semaphore有点相似,内部有一个许可,park的时候拿掉这个许可,unpark的时候申请这个许可。所以如果unpark在park之前,是不会发生线程冻结的。

下面的代码是[高并发Java 二] 多线程基础中suspend示例代码,在使用suspend时会发生死锁。

而使用 LockSupport则不会发生死锁。

另外

park()能够响应中断,但不抛出异常。中断响应的结果是,park()函数的返回,可以从Thread.interrupted()得到中断标志。

在JDK当中有大量地方使用到了park,当然LockSupport的实现也是使用unsafe.park()来实现的。

public static void park() {        unsafe.park(false, 0L);

}

1.8 ReentrantLock 的实现

下面来介绍下ReentrantLock的实现,ReentrantLock的实现主要由3部分组成:

CAS状态

等待队列

park()

ReentrantLock的父类中会有一个state变量来表示同步的状态

/**

    * The synchronization state.

    */

   private volatile int state;

通过CAS操作来设置state来获取锁,如果设置成了1,则将锁的持有者给当前线程

final void lock() {            if (compareAndSetState(0, 1))

               setExclusiveOwnerThread(Thread.currentThread());            else

               acquire(1);

       }

如果拿锁不成功,则会做一个申请

public final void acquire(int arg) {        if (!tryAcquire(arg)

           acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

           selfInterrupt();

   }

首先,再去申请下试试看tryAcquire,因为此时可能另一个线程已经释放了锁。

如果还是没有申请到锁,就addWaiter,意思是把自己加到等待队列中去

其间还会有多次尝试去申请锁,如果还是申请不到,就会被挂起

private final boolean parkAndCheckInterrupt() {

       LockSupport.park(this);        return Thread.interrupted();

   }

同理,如果在unlock操作中,就是释放了锁,然后unpark,这里就不具体讲了。

2. 并发容器及典型源码分析

2.1 ConcurrentHashMap

我们知道HashMap不是一个线程安全的容器,最简单的方式使HashMap变成线程安全就是使用Collections.synchronizedMap,它是对HashMap的一个包装

public static Map m=Collections.synchronizedMap(new HashMap());

同理对于List,Set也提供了相似方法。

但是这种方式只适合于并发量比较小的情况。

我们来看下synchronizedMap的实现

它会将HashMap包装在里面,然后将HashMap的每个操作都加上synchronized。

由于每个方法都是获取同一把锁(mutex),这就意味着,put和remove等操作是互斥的,大大减少了并发量。

下面来看下ConcurrentHashMap是如何实现的

在 ConcurrentHashMap内部有一个Segment段,它将大的HashMap切分成若干个段(小的HashMap),然后让数据在每一段上Hash,这样多个线程在不同段上的Hash操作一定是线程安全的,所以只需要同步同一个段上的线程就可以了,这样实现了锁的分离,大大增加了并发量。

在使用ConcurrentHashMap.size时会比较麻烦,因为它要统计每个段的数据和,在这个时候,要把每一个段都加上锁,然后再做数据统计。这个就是把锁分离后的小小弊端,但是size方法应该是不会被高频率调用的方法。

在实现上,不使用synchronized和lock.lock而是尽量使用trylock,同时在HashMap的实现上,也做了一点优化。这里就不提了。

2.2 BlockingQueue

BlockingQueue不是一个高性能的容器。但是它是一个非常好的共享数据的容器。是典型的生产者和消费者的实现。

JAVA高并发问题,大数据,频繁I/O操作。

建议采用缓存处理,按照你说的这种数据量,基于redis的缓存完全可以满足,存取速度可以10W+的,另外,拟采用的hashMap 是ConcurrentHashMap还是其他,页面展示是增量查询还是直接所有的再查询一次,socket数据接收你是用的netty还是mina,这都需要经过仔细的斟酌考虑设计的。有这么大的并发的需求,完全可以考虑做分布式集群的,估计这只是领导想要的目标吧

java并发包有哪些类

1、CyclicBarrier

一个同步辅助类,允许一组线程相互等待,直到这组线程都到达某个公共屏障点。该barrier在释放等待线程后可以重用,因此称为循环的barrier。

来个示例:

[java] view plain copy

package test;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class Recipes_CyclicBarrier {

public static CyclicBarrier barrier = new CyclicBarrier(10);

public static void main(String[] args){

ExecutorService executor = Executors.newCachedThreadPool();//FixedThreadPool(10);

for(int i=1;i=10;i++){

executor.submit(new Thread(new Runner(i+"号选手")));

}

executor.shutdown();

}

}

class Runner implements Runnable{

private String name;

public Runner(String name){

this.name = name;

}

@Override

public void run() {

System.out.println(name + "准备好了。");

try {

Recipes_CyclicBarrier.barrier.await();  //此处就是公共屏障点,所有线程到达之后,会释放所有等待的线程

} catch (Exception e) {

}

System.out.println(name + "起跑!");

}

}

2、CountDownLatch

CountDownLatch和CyclicBarrier有点类似,但是还是有些区别的。CountDownLatch也是一个同步辅助类,它允许一个或者多个线程一直等待,直到正在其他线程中执行的操作完成。它是等待正在其他线程中执行的操作,并不是线程之间相互等待。CountDownLatch初始化时需要给定一个计数值,每个线程执行完之后,必须调用countDown()方法使计数值减1,直到计数值为0,此时等待的线程才会释放。

来个示例:

[java] view plain copy

package test;

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class CountDownLatchDemo {

public static CountDownLatch countDownLatch = new CountDownLatch(10);//初始化计数值

public static void main(String[] args){

ExecutorService executor = Executors.newCachedThreadPool();//FixedThreadPool(10);

for(int i=1;i=10;i++){

executor.submit(new Thread(new Runner1(i+"号选手")));

}

executor.shutdown();

}

}

class Runner1 implements Runnable{

private String name;

public Runner1(String name){

this.name = name;

}

@Override

public void run() {

System.out.println(name + "准备好了。");

CountDownLatchDemo.countDownLatch.countDown();  //计数值减1

try {

CountDownLatchDemo.countDownLatch.await();

} catch (Exception e) {

}

System.out.println(name + "起跑!");

}

}

3、CopyOnWriteArrayList CopyOnWriteArraySet

CopyOnWriteArrayList CopyOnWriteArraySet是并发容器,适合读多写少的场景,如网站的黑白名单设置。缺点是内存占用大,数据一致性的问题,CopyOnWrite容器只能保证数据最终的一致性,不能保证数据实时一致性。鉴于它的这些缺点,可以使用ConcurrentHashMap容器。

实现原理:新增到容器的数据会放到一个新的容器中,然后将原容器的引用指向新容器,旧容器也会存在,因此会有两个容器占用内存。我们也可以用同样的方式实现自己的CopyOnWriteMap。

4、ConcurrentHashMap

ConcurrentHashMap同样是一个并发容器,将同步粒度最小化。

实现原理:ConcurrentHashMap默认是由16个Segment组成,每个Segment由多个Hashtable组成,数据变更需要经过两次哈希算法,第一次哈希定位到Segment,第二次哈希定位到Segment下的Hashtable,容器只会将单个Segment锁住,然后操作Segment下的Hashtable,多个Segment之间不受影响。如果需要扩容不是对Segment扩容而是对Segment下的Hashtable扩容。虽然经过两次哈希算法会使效率降低,但是比锁住整个容器效率要高得多。

5、BlockingQueue

BlockingQueue只是一个接口,它的实现类有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、DelayQueue、LinkedBlockingDeque。

ArrayBlockingQueue:由数据支持的有界阻塞队列。

LinkedBlockingQueue:基于链接节点、范围任意的阻塞队列。

PriorityBlockingQueue:无界阻塞队列。

SynchronousQueue:一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作。

DelayQueue:Delayed元素的一个无界阻塞队列。

LinkedBlockingDeque:基于链接节点、范围任意的双端阻塞队列,可以在队列的两端添加、移除元素。

6、Lock

Lock分为公平锁和非公平锁,默认是非公平锁。实现类有ReetrantLock、ReetrantReadWriteLock,都依赖于AbstractQueuedSynchronizer抽象类。ReetrantLock将所有Lock接口的操作都委派到Sync类上,Sync有两个子类:NonFairSync和FaiSync,通过其命名就能知道分别处理非公平锁和公平锁的。AbstractQueuedSynchronizer把所有请求构成一个CLH队列,这里是一个虚拟队列,当有线程竞争锁时,该线程会首先尝试是否能获取锁,这种做法对于在队列中等待的线程来说是非公平的,如果有线程正在Running,那么通过循环的CAS操作将此线程增加到队尾,直至添加成功。

7、Atomic包

Atomic包下的类实现了原子操作,有对基本类型如int、long、boolean实现原子操作的类:AtomicInteger、AtomicLong、AtomicBoolean,如果需要对一个对象进行原子操作,也有对对象引用进行原子操作的AtomicReference类,还有对对象数组操作的原子类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。原子操作核心思想是CAS操作,然后调用底层操作系统指令来实现。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。