欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 高并發(fā)五:JDK并發(fā)包1詳細(xì)介紹

 更新時(shí)間:2016年09月09日 17:46:20   作者:Hosee  
本文主要介紹 Java高并發(fā)JDK并發(fā)包1的資料,這里對(duì)1.各種同步控制工具的使用 2.并發(fā)容器及典型源碼分析,有需要的小伙伴可以參考下

在[高并發(fā)Java 二] 多線程基礎(chǔ)中,我們已經(jīng)初步提到了基本的線程同步操作。這次要提到的是在并發(fā)包中的同步控制工具。

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

1.1 ReentrantLock

ReentrantLock感覺(jué)上是synchronized的增強(qiáng)版,synchronized的特點(diǎn)是使用簡(jiǎn)單,一切交給JVM去處理,但是功能上是比較薄弱的。在JDK1.5之前,ReentrantLock的性能要好于synchronized,由于對(duì)JVM進(jìn)行了優(yōu)化,現(xiàn)在的JDK版本中,兩者性能是不相上下的。如果是簡(jiǎn)單的實(shí)現(xiàn),不要刻意去使用ReentrantLock。

相比于synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時(shí)、公平鎖等特點(diǎn)。

首先我們通過(guò)一個(gè)例子來(lái)說(shuō)明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);
 }

}

有兩個(gè)線程都對(duì)i進(jìn)行++操作,為了保證線程安全,使用了 ReentrantLock,從用法上可以看出,與 synchronized相比, ReentrantLock就稍微復(fù)雜一點(diǎn)。因?yàn)楸仨氃趂inally中進(jìn)行解鎖操作,如果不在 finally解鎖,有可能代碼出現(xiàn)異常鎖沒(méi)被釋放,而synchronized是由JVM來(lái)釋放鎖。

那么ReentrantLock到底有哪些優(yōu)秀的特點(diǎn)呢?

1.1.1 可重入

單線程可以重復(fù)進(jìn)入,但要重復(fù)退出

lock.lock();
lock.lock();
try
{
 i++;
 
} 
finally
{
 lock.unlock();
 lock.unlock();
}

由于ReentrantLock是重入鎖,所以可以反復(fù)得到相同的一把鎖,它有一個(gè)與鎖相關(guān)的獲取計(jì)數(shù)器,如果擁有鎖的某個(gè)線程再次得到鎖,那么獲取計(jì)數(shù)器就加1,然后鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。這模仿了 synchronized 的語(yǔ)義;如果線程進(jìn)入由線程已經(jīng)擁有的監(jiān)控器保護(hù)的 synchronized 塊,就允許線程繼續(xù)進(jìn)行,當(dāng)線程退出第二個(gè)(或者后續(xù)) synchronized 塊的時(shí)候,不釋放鎖,只有線程退出它進(jìn)入的監(jiān)控器保護(hù)的第一個(gè)synchronized 塊時(shí),才釋放鎖。

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(); // 調(diào)用自己類(lèi)中其他的synchronized方法
 }
 
 private synchronized void doAnotherThing() {
 super.doSomething(); // 調(diào)用父類(lèi)的synchronized方法
 System.out.println("3child.doAnotherThing()");
 }
 
 @Override
 public void run() {
 child.doSomething();
 }
}
class Father {
 public synchronized void doSomething() {
 System.out.println("2father.doSomething()");
 }
}

我們可以看到一個(gè)線程進(jìn)入不同的 synchronized方法,是不會(huì)釋放之前得到的鎖的。所以輸出還是順序輸出。所以synchronized也是重入鎖

輸出:

1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
...

1.1.2.可中斷

與synchronized不同的是,ReentrantLock對(duì)中斷是有響應(yīng)的。中斷相關(guān)知識(shí)查看[高并發(fā)Java 二] 多線程基礎(chǔ)

普通的lock.lock()是不能響應(yīng)中斷的,lock.lockInterruptibly()能夠響應(yīng)中斷。

我們模擬出一個(gè)死鎖現(xiàn)場(chǎng),然后用中斷來(lái)處理死鎖

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();
 }
 }

}

上述代碼有可能會(huì)發(fā)生死鎖,線程1得到lock1,線程2得到lock2,然后彼此又想獲得對(duì)方的鎖。

我們用jstack查看運(yùn)行上述代碼后的情況

的確發(fā)現(xiàn)了一個(gè)死鎖。

DeadlockChecker.check();方法用來(lái)檢測(cè)死鎖,然后把死鎖的線程中斷。中斷后,線程正常退出。

1.1.3.可限時(shí)

超時(shí)不能獲得鎖,就返回false,不會(huì)永久等待構(gòu)成死鎖

使用lock.tryLock(long timeout, TimeUnit unit)來(lái)實(shí)現(xiàn)可限時(shí)鎖,參數(shù)為時(shí)間和單位。

舉個(gè)例子來(lái)說(shuō)明下可限時(shí):

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();
 }

}

使用兩個(gè)線程來(lái)爭(zhēng)奪一把鎖,當(dāng)某個(gè)線程獲得鎖后,sleep6秒,每個(gè)線程都只嘗試5秒去獲得鎖。

所以必定有一個(gè)線程無(wú)法獲得鎖。無(wú)法獲得后就直接退出了。

輸出:

get lock failed

1.1.4.公平鎖

使用方式:

public ReentrantLock(boolean fair)

public static ReentrantLock fairLock = new ReentrantLock(true);

一般意義上的鎖是不公平的,不一定先來(lái)的線程能先得到鎖,后來(lái)的線程就后得到鎖。不公平的鎖可能會(huì)產(chǎn)生饑餓現(xiàn)象。

公平鎖的意思就是,這個(gè)鎖能保證線程是先來(lái)的先得到鎖。雖然公平鎖不會(huì)產(chǎn)生饑餓現(xiàn)象,但是公平鎖的性能會(huì)比非公平鎖差很多。

1.2 Condition

Condition與ReentrantLock的關(guān)系就類(lèi)似于synchronized與Object.wait()/signal()

await()方法會(huì)使當(dāng)前線程等待,同時(shí)釋放當(dāng)前鎖,當(dāng)其他線程中使用signal()時(shí)或者signalAll()方法時(shí),線 程會(huì)重新獲得鎖并繼續(xù)執(zhí)行。或者當(dāng)線程被中斷時(shí),也能跳出等待。這和Object.wait()方法很相似。

awaitUninterruptibly()方法與await()方法基本相同,但是它并不會(huì)再等待過(guò)程中響應(yīng)中斷。 singal()方法用于喚醒一個(gè)在等待中的線程。相對(duì)的singalAll()方法會(huì)喚醒所有在等待中的線程。這和Obejct.notify()方法很類(lèi)似。

這里就不再詳細(xì)介紹了。舉個(gè)例子來(lái)說(shuō)明:

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();
 }

}

上述例子很簡(jiǎn)單,讓一個(gè)線程await住,讓主線程去喚醒它。condition.await()/signal只能在得到鎖以后使用。

1.3.Semaphore

對(duì)于鎖來(lái)說(shuō),它是互斥的排他的。意思就是,只要我獲得了鎖,沒(méi)人能再獲得了。

而對(duì)于Semaphore來(lái)說(shuō),它允許多個(gè)線程同時(shí)進(jìn)入臨界區(qū)??梢哉J(rèn)為它是一個(gè)共享鎖,但是共享的額度是有限制的,額度用完了,其他沒(méi)有拿到額度的線程還是要阻塞在臨界區(qū)外。當(dāng)額度為1時(shí),就相等于lock

下面舉個(gè)例子:

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);
 }
 }

}

有一個(gè)20個(gè)線程的線程池,每個(gè)線程都去 Semaphore的許可,Semaphore的許可只有5個(gè),運(yùn)行后可以看到,5個(gè)一批,一批一批地輸出。

當(dāng)然一個(gè)線程也可以一次申請(qǐng)多個(gè)許可

public void acquire(int permits) throws InterruptedException

1.4 ReadWriteLock

ReadWriteLock是區(qū)分功能的鎖。讀和寫(xiě)是兩種不同的功能,讀-讀不互斥,讀-寫(xiě)互斥,寫(xiě)-寫(xiě)互斥。

這樣的設(shè)計(jì)是并發(fā)量提高了,又保證了數(shù)據(jù)安全。

使用方式:

private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();

詳細(xì)例子可以查看 Java實(shí)現(xiàn)生產(chǎn)者消費(fèi)者問(wèn)題與讀者寫(xiě)者問(wèn)題,這里就不展開(kāi)了。

1.5 CountDownLatch

倒數(shù)計(jì)時(shí)器
一種典型的場(chǎng)景就是火箭發(fā)射。在火箭發(fā)射前,為了保證萬(wàn)無(wú)一失,往往還要進(jìn)行各項(xiàng)設(shè)備、儀器的檢查。 只有等所有檢查完畢后,引擎才能點(diǎn)火。這種場(chǎng)景就非常適合使用CountDownLatch。它可以使得點(diǎn)火線程
,等待所有檢查線程全部完工后,再執(zhí)行

使用方式:

static final CountDownLatch end = new CountDownLatch(10);
end.countDown();
end.await();

示意圖:

一個(gè)簡(jiǎn)單的例子:

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個(gè)線程全部執(zhí)行完才會(huì)輸出"end"。

1.6 CyclicBarrier

和CountDownLatch相似,也是等待某些線程都做完以后再執(zhí)行。與CountDownLatch區(qū)別在于這個(gè)計(jì)數(shù)器可以反復(fù)使用。比如,假設(shè)我們將計(jì)數(shù)器設(shè)置為10。那么湊齊第一批1 0個(gè)線程后,計(jì)數(shù)器就會(huì)歸零,然后接著湊齊下一批10個(gè)線程

使用方式:

public CyclicBarrier(int parties, Runnable barrierAction)

barrierAction就是當(dāng)計(jì)數(shù)器一次計(jì)數(shù)完成后,系統(tǒng)會(huì)執(zhí)行的動(dòng)作

await()

示意圖:

下面舉個(gè)例子:

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 + "個(gè)任務(wù)完成");
 }
 else
 {
 System.out.println(n + "個(gè)集合完成");
 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 + "報(bào)道");
 threads[i] = new Thread(new Test("士兵" + i, barrier));
 threads[i].start();
 }
 }

}

打印結(jié)果:

集合
0報(bào)道
1報(bào)道
2報(bào)道
3報(bào)道
4報(bào)道
5報(bào)道
6報(bào)道
7報(bào)道
8報(bào)道
9報(bào)道
10個(gè)集合完成
士兵5: done
士兵7: done
士兵8: done
士兵3: done
士兵4: done
士兵1: done
士兵6: done
士兵2: done
士兵0: done
士兵9: done
10個(gè)任務(wù)完成

1.7 LockSupport

提供線程阻塞原語(yǔ)

和suspend類(lèi)似

LockSupport.park();
LockSupport.unpark(t1);

與suspend相比 不容易引起線程凍結(jié)

LockSupport的思想呢,和 Semaphore有點(diǎn)相似,內(nèi)部有一個(gè)許可,park的時(shí)候拿掉這個(gè)許可,unpark的時(shí)候申請(qǐng)這個(gè)許可。所以如果unpark在park之前,是不會(huì)發(fā)生線程凍結(jié)的。

下面的代碼是[高并發(fā)Java 二] 多線程基礎(chǔ)中suspend示例代碼,在使用suspend時(shí)會(huì)發(fā)生死鎖。

package test;

import java.util.concurrent.locks.LockSupport;
 
public class Test
{
 static Object u = new Object();
 static TestSuspendThread t1 = new TestSuspendThread("t1");
 static TestSuspendThread t2 = new TestSuspendThread("t2");
 
 public static class TestSuspendThread extends Thread
 {
 public TestSuspendThread(String name)
 {
  setName(name);
 }
 
 @Override
 public void run()
 {
  synchronized (u)
  {
  System.out.println("in " + getName());
  //Thread.currentThread().suspend();
  LockSupport.park();
  }
 }
 }
 
 public static void main(String[] args) throws InterruptedException
 {
 t1.start();
 Thread.sleep(100);
 t2.start();
// t1.resume();
// t2.resume();
 LockSupport.unpark(t1);
 LockSupport.unpark(t2);
 t1.join();
 t2.join();
 }
}

而使用 LockSupport則不會(huì)發(fā)生死鎖。

另外

park()能夠響應(yīng)中斷,但不拋出異常。中斷響應(yīng)的結(jié)果是,park()函數(shù)的返回,可以從Thread.interrupted()得到中斷標(biāo)志。

在JDK當(dāng)中有大量地方使用到了park,當(dāng)然LockSupport的實(shí)現(xiàn)也是使用unsafe.park()來(lái)實(shí)現(xiàn)的。

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

1.8 ReentrantLock 的實(shí)現(xiàn)

下面來(lái)介紹下ReentrantLock的實(shí)現(xiàn),ReentrantLock的實(shí)現(xiàn)主要由3部分組成:

  1. CAS狀態(tài)
  2. 等待隊(duì)列
  3. park()

ReentrantLock的父類(lèi)中會(huì)有一個(gè)state變量來(lái)表示同步的狀態(tài)

/**
 * The synchronization state.
 */
 private volatile int state;

通過(guò)CAS操作來(lái)設(shè)置state來(lái)獲取鎖,如果設(shè)置成了1,則將鎖的持有者給當(dāng)前線程

final void lock() {
  if (compareAndSetState(0, 1))
  setExclusiveOwnerThread(Thread.currentThread());
  else
  acquire(1);
 }

如果拿鎖不成功,則會(huì)做一個(gè)申請(qǐng)

public final void acquire(int arg) {
 if (!tryAcquire(arg) &&
  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  selfInterrupt();
 }

首先,再去申請(qǐng)下試試看tryAcquire,因?yàn)榇藭r(shí)可能另一個(gè)線程已經(jīng)釋放了鎖。

如果還是沒(méi)有申請(qǐng)到鎖,就addWaiter,意思是把自己加到等待隊(duì)列中去

private Node addWaiter(Node mode) {
 Node node = new Node(Thread.currentThread(), mode);
 // Try the fast path of enq; backup to full enq on failure
 Node pred = tail;
 if (pred != null) {
  node.prev = pred;
  if (compareAndSetTail(pred, node)) {
  pred.next = node;
  return node;
  }
 }
 enq(node);
 return node;
 }

其間還會(huì)有多次嘗試去申請(qǐng)鎖,如果還是申請(qǐng)不到,就會(huì)被掛起

private final boolean parkAndCheckInterrupt() {
 LockSupport.park(this);
 return Thread.interrupted();
 }

同理,如果在unlock操作中,就是釋放了鎖,然后unpark,這里就不具體講了。

2. 并發(fā)容器及典型源碼分析

2.1 ConcurrentHashMap

我們知道HashMap不是一個(gè)線程安全的容器,最簡(jiǎn)單的方式使HashMap變成線程安全就是使用

Collections.synchronizedMap,它是對(duì)HashMap的一個(gè)包裝

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

同理對(duì)于List,Set也提供了相似方法。

但是這種方式只適合于并發(fā)量比較小的情況。

我們來(lái)看下synchronizedMap的實(shí)現(xiàn)

private final Map<K,V> m; // Backing Map
 final Object mutex; // Object on which to synchronize

 SynchronizedMap(Map<K,V> m) {
  if (m==null)
  throw new NullPointerException();
  this.m = m;
  mutex = this;
 }

 SynchronizedMap(Map<K,V> m, Object mutex) {
  this.m = m;
  this.mutex = mutex;
 }

 public int size() {
  synchronized (mutex) {return m.size();}
 }
 public boolean isEmpty() {
  synchronized (mutex) {return m.isEmpty();}
 }
 public boolean containsKey(Object key) {
  synchronized (mutex) {return m.containsKey(key);}
 }
 public boolean containsValue(Object value) {
  synchronized (mutex) {return m.containsValue(value);}
 }
 public V get(Object key) {
  synchronized (mutex) {return m.get(key);}
 }

 public V put(K key, V value) {
  synchronized (mutex) {return m.put(key, value);}
 }
 public V remove(Object key) {
  synchronized (mutex) {return m.remove(key);}
 }
 public void putAll(Map<? extends K, ? extends V> map) {
  synchronized (mutex) {m.putAll(map);}
 }
 public void clear() {
  synchronized (mutex) {m.clear();}
 }

它會(huì)將HashMap包裝在里面,然后將HashMap的每個(gè)操作都加上synchronized。

由于每個(gè)方法都是獲取同一把鎖(mutex),這就意味著,put和remove等操作是互斥的,大大減少了并發(fā)量。

下面來(lái)看下ConcurrentHashMap是如何實(shí)現(xiàn)的

public V put(K key, V value) {
 Segment<K,V> s;
 if (value == null)
  throw new NullPointerException();
 int hash = hash(key);
 int j = (hash >>> segmentShift) & segmentMask;
 if ((s = (Segment<K,V>)UNSAFE.getObject  // nonvolatile; recheck
  (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
  s = ensureSegment(j);
 return s.put(key, hash, value, false);
 }

在 ConcurrentHashMap內(nèi)部有一個(gè)Segment段,它將大的HashMap切分成若干個(gè)段(小的HashMap),然后讓數(shù)據(jù)在每一段上Hash,這樣多個(gè)線程在不同段上的Hash操作一定是線程安全的,所以只需要同步同一個(gè)段上的線程就可以了,這樣實(shí)現(xiàn)了鎖的分離,大大增加了并發(fā)量。

在使用ConcurrentHashMap.size時(shí)會(huì)比較麻煩,因?yàn)樗y(tǒng)計(jì)每個(gè)段的數(shù)據(jù)和,在這個(gè)時(shí)候,要把每一個(gè)段都加上鎖,然后再做數(shù)據(jù)統(tǒng)計(jì)。這個(gè)就是把鎖分離后的小小弊端,但是size方法應(yīng)該是不會(huì)被高頻率調(diào)用的方法。

在實(shí)現(xiàn)上,不使用synchronized和lock.lock而是盡量使用trylock,同時(shí)在HashMap的實(shí)現(xiàn)上,也做了一點(diǎn)優(yōu)化。這里就不提了。

2.2 BlockingQueue

BlockingQueue不是一個(gè)高性能的容器。但是它是一個(gè)非常好的共享數(shù)據(jù)的容器。是典型的生產(chǎn)者和消費(fèi)者的實(shí)現(xiàn)。

示意圖:

 

具體可以查看Java實(shí)現(xiàn)生產(chǎn)者消費(fèi)者問(wèn)題與讀者寫(xiě)者問(wèn)題

相關(guān)文章

  • Java二維碼登錄流程實(shí)現(xiàn)代碼(包含短地址生成,含部分代碼)

    Java二維碼登錄流程實(shí)現(xiàn)代碼(包含短地址生成,含部分代碼)

    近年來(lái),二維碼的使用越來(lái)越風(fēng)生水起,本篇文章主要介紹了Java二維碼登錄流程實(shí)現(xiàn)代碼,其中包含短地址生成,有興趣的可以了解一下。
    2016-12-12
  • Maven引入本地Jar包并打包進(jìn)War包中的方法

    Maven引入本地Jar包并打包進(jìn)War包中的方法

    本篇文章主要介紹了Maven引入本地Jar包并打包進(jìn)War包中的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • java性能優(yōu)化之分代回收

    java性能優(yōu)化之分代回收

    這篇文章主要介紹了java性能優(yōu)化之分代回收,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • Java創(chuàng)建線程三種方式的優(yōu)缺點(diǎn)

    Java創(chuàng)建線程三種方式的優(yōu)缺點(diǎn)

    今天小編就為大家分享一篇關(guān)于Java創(chuàng)建線程三種方式的優(yōu)缺點(diǎn),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • 詳解Java 中的UnitTest 和 PowerMock

    詳解Java 中的UnitTest 和 PowerMock

    這篇文章主要介紹了Java中的 UnitTest 和 PowerMock,文中講解非常詳細(xì),對(duì)大家學(xué)習(xí)有很大的幫助,感興趣的朋友可以了解下
    2020-06-06
  • 基于Java將Excel科學(xué)計(jì)數(shù)法解析成數(shù)字

    基于Java將Excel科學(xué)計(jì)數(shù)法解析成數(shù)字

    這篇文章主要介紹了基于Java將Excel科學(xué)計(jì)數(shù)法解析成數(shù)字,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java基于HttpClient實(shí)現(xiàn)RPC的示例

    Java基于HttpClient實(shí)現(xiàn)RPC的示例

    HttpClient可以實(shí)現(xiàn)使用Java代碼完成標(biāo)準(zhǔn)HTTP請(qǐng)求及響應(yīng)。本文主要介紹了Java基于HttpClient實(shí)現(xiàn)RPC,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • SpringBoot使用Kaptcha實(shí)現(xiàn)驗(yàn)證碼的生成與驗(yàn)證功能

    SpringBoot使用Kaptcha實(shí)現(xiàn)驗(yàn)證碼的生成與驗(yàn)證功能

    這篇文章主要介紹了SpringBoot使用Kaptcha實(shí)現(xiàn)驗(yàn)證碼的生成與驗(yàn)證功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Java中使用Closeable接口自動(dòng)關(guān)閉資源詳解

    Java中使用Closeable接口自動(dòng)關(guān)閉資源詳解

    這篇文章主要介紹了Java中使用Closeable接口自動(dòng)關(guān)閉資源詳解,Closeable接口繼承于AutoCloseable,主要的作用就是自動(dòng)的關(guān)閉資源,其中close()方法是關(guān)閉流并且釋放與其相關(guān)的任何方法,如果流已被關(guān)閉,那么調(diào)用此方法沒(méi)有效果,需要的朋友可以參考下
    2023-12-12
  • java中Class.forName方法的作用詳解

    java中Class.forName方法的作用詳解

    Class.forName(xxx.xx.xx) 返回的是一個(gè)類(lèi),但Class.forName方法的作用到底是什么終?下面這篇文章就來(lái)給大家詳細(xì)介紹了關(guān)于java中Class.forName方法的作用,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-06-06

最新評(píng)論