0)理由:
当初、プログラムのパフォーマンス要件が低かった時代では、基本的にシングルスレッドプログラミングでしたが、当時のCPUは基本的にシングルコアでしたが、コンピューターハードウェアテクノロジーの発達により、マルチコアCPUが登場し、過去のシングルコアプログラミングではそれをうまく利用できなくなっていましたCPUコアの数と、プログラムの高速化およびパフォーマンス要件の増加が相まって、並行プログラミングテクノロジーが登場しています。シングルコアプログラムのパフォーマンスボトルネックは頻繁なコンテキスト切り替えであり、コンテキスト切り替えを減らすことでパフォーマンスを向上させることができます。より多くのCPUが非同期で連携するようにします。
1)開発プロセス:
Javaは、Java5に追加された同時実行ツールキットです。このパッケージには、Javaでの並行プログラミングを容易にする一連のクラスが含まれています。その前に、関連するツールクラスを手動で実装する必要があります。パッケージパスは、ツールクラスの形式でjava.util.concurrentとして提供されます。
提供されるクラス:
- BlockingQueue
- ArrayBlockingQueue
- DelayQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- 同期キュー
- ブロッキングデキュー
- チェーンブロックされた両端キューLinkedBlockingDeque
- 並行マップConcurrentMap
- ConcurrentNavigableMap
- ブロックConutDownLatch
- サイクリックバリア
- 交換器
- セマフォ
- ExecutorService
- ThreadPoolExecutor
- スケジュールされたExecutorService
- フォークとマージのためのForkJoinPool
- ロックする
- ReadWriteLock
- AtomicBoolean
- AtomicInteger
- AtomicLong
- AtomicReference
概要には一般的に使用されるものを選択します。
2)必要な理論的知識
悲観的ロックは
常に最悪のケースを想定しています。データを取得するたびに、他の人がそれを変更すると思うので、データを取得するたびにデータがロックされ、他の人がロックを取得するまでブロックされます(共有リソースは一度に1つのスレッドによってのみ使用され、他のスレッドはブロックされ、リソースは使い尽くされた後に他のスレッドに転送されます。行ロック、テーブルロック、読み取りロック、書き込みロックなど、操作を実行する前にロックされる従来のリレーショナルデータベースでは、このようなロックメカニズムの多くが使用されています。Javaでの同期やReentrantLockなどの排他ロックは、悲観的なロックのアイデアの実現です。
オプティミスティックロックは
常に最良の状況を想定しています。データを取得するたびに、他のユーザーがデータを変更しないと考えられるため、データはロックされませんが、更新時には、この期間中に他のユーザーがこのデータを更新したかどうかを判断します。バージョン番号メカニズムとCASアルゴリズムを使用して実現します。オプティミスティックロックは、スループットを向上させることができる複数読み取りアプリケーションタイプに適しています。データベースによって提供されるwrite_conditionメカニズムと同様に、これらは実際にはオプティミスティックロックです。Javaでは、java.util.concurrent.atomicパッケージの下のアトミック変数クラスは、楽観的ロックの実装であるCASを使用します。
CASは楽観的ロックテクノロジーです。CASを使用して複数のスレッドが同じ変数を同時に更新しようとすると、変数の値を更新できるのは1つのスレッドだけですが、他のスレッドは失敗します。失敗したスレッドは中断されませんが、通知されますこのコンテストに失敗し、再試行できます
3)最新の開発:
Javaクラスライブラリに表示される最初の関連するコレクションクラスHashtable
は、JDK 1.0の一部であることです。Hashtable
使いやすく、スレッドセーフな、関連付けられたマップ関数を提供しますが、もちろん便利です。ただし、スレッドセーフティには代償が伴いますHashtable
。すべてのメソッドが同期されます。現時点では、同期が競合しないとパフォーマンスにかなりのコストがかかります。Hashtable
後継HashMap
基本クラスと同期ラッパー同期が提供することによって、発生するフレームJDK1.2のセットの一部としてCollections.synchronizedMap
スレッドの安全性の問題を解決します。
しかし:結果は、これらのプログラムの表面上のが、負荷が軽いとき正常に動作しますが、負荷が重いがあると、彼らはスローし始めるということですNullPointerException
かConcurrentModificationException 异常
随后出现了 ConcurrentMap
ConcurrentMap 的实现
concurrent 包里面就一个类实现了 ConcurrentMap 接口
ConcurrentHashMap
ConcurrentHashMap
ConcurrentHashMap 和 HashTable 类很相似,但 ConcurrentHashMap 能提供比 HashTable 更好的并发性能。在你从中读取对象的时候,ConcurrentHashMap 并不会把整个 Map 锁住。此外,在你向其写入对象的时候,ConcurrentHashMap 也不会锁住整个 Map,它的内部只是把 Map 中正在被写入的部分锁定。
其实就是把 synchronized 同步整个方法改为了同步方法里面的部分代码。
另外一个不同点是,在被遍历的时候,即使是 ConcurrentHashMap 被改动,它也不会抛 ConcurrentModificationException。尽管 Iterator 的设计不是为多个线程同时使用。
使用例子:
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// HashMap<String, String> map = new HashMap<>();
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("1", "a");
map.put("2", "b");
map.put("3", "c");
map.put("4", "d");
map.put("5", "e");
map.put("6", "f");
map.put("7", "g");
map.put("8", "h");
new Thread1(map).start();
new Thread2(map).start();
}
}
class Thread1 extends Thread {
private final Map map;
Thread1(Map map) {
this.map = map;
}
@Override
public void run() {
super.run();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.remove("6");
}
}
class Thread2 extends Thread {
private final Map map;
Thread2(Map map) {
this.map = map;
}
@Override
public void run() {
super.run();
Set set = map.keySet();
for (Object next : set) {
System.out.println(next + ":" + map.get(next));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}复制代码
CopyOnWriteArrayList
在那些遍历操作大大地多于插入或移除操作的并发应用程序中,一般用
CopyOnWriteArrayList
类替代 ArrayList
。如果是用于存放一个侦听器(listener)列表,例如在AWT或Swing应用程序中,或者在常见的JavaBean中,那么这种情况很常见(相关的 CopyOnWriteArraySet
使用一个 CopyOnWriteArrayList
来实现 Set
接口) 。
原理实现:
如果您正在使用一个普通的
ArrayList
来存放一个侦听器列表,那么只要该列表是可变的,而且可能要被多个线程访问,您 就必须要么在对其进行迭代操作期间,要么在迭代前进行的克隆操作期间,锁定整个列表,这两种做法的开销都很大。当对列表执行会引起列表发生变化的操作时, CopyOnWriteArrayList
并不是为列表创建一个全新的副本,它的迭代器肯定能够返回在迭代器被创建时列表的状态,而不会抛出 ConcurrentModificationException
。在对列表进行迭代之前不必克隆列表或者在迭代期间锁 定列表,因为迭代器所看到的列表的副本是不变的。换句话说, CopyOnWriteArrayList
含有对一个不可变数组的一个可变的引用,因此,只要保留好那个引用,您就可以获得不可变的线程安全性的好处,而且不用锁 定列表。
4)目前最新应用情况:
例としてConcurrentHashMapを取り上げます。
jdk7バージョン
ConcurrentHashMapとHashMapは同様の設計思想を持っていますが、並行操作をサポートするために、いくつかの改善が行われています。ConcurrentHashMapはセグメントの概念を導入し、目的はマップを複数のセグメント(デフォルトは16)に分割することです。操作ConcurrentHashMapは、特定のセグメントを操作するように改良されています。マルチスレッド環境では、異なるスレッドが異なるセグメントを操作し、それらは互いに影響を与えないため、並行操作を実現できます。
最新のJDk8実装:
jdk7バージョンと比較して、jdk8バージョンのConcurrentHashMapは多くの変更を送信しました。jdk8はセグメントの設計を直接放棄し、軽量のノード+ CAS +同期設計を採用してスレッドの安全性を確保しました。
ConcurrentHashMap構造図
上記のConcurrentHashMapの一般的な構造を見てください。ノード配列で、デフォルトは16で、自動的に拡張できます。拡張速度は0.75です。
private static finalint DEFAULT_CONCURRENCY_LEVEL = 16;
private static final float LOAD_FACTOR = 0.75f;
各ノードはリンクリストをマウントします。リンクリストのマウントデータが8を超えると、リンクリストは自動的に赤黒ツリーに変換されます
static final int TREEIFY_THRESHOLD = 8;
コードの一部:
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
transient volatile Node<K,V>[] table;
}
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
}
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;
volatile TreeNode<K,V> first;
volatile Thread waiter;
volatile int lockState;
}