この記事では、主に Java スレッド セーフと非スレッド セーフの分析を紹介します。これには、非スレッド セーフ現象のシミュレーションとスレッド セーフの実装が含まれます。必要な友人はそれを参照し、交換して一緒に学ぶことができます。
ArrayList と Vector の違いは何ですか?ハッシュマップとハッシュテーブルの違いは何ですか? StringBuilder と StringBuffer の違いは何ですか?これらは Java の面接でよくある基本的な質問です。このような質問に直面すると、答えは次のようになります。ArrayList は非スレッド セーフ、Vector はスレッド セーフ、HashMap は非スレッド セーフ、HashTable は非スレッド セーフ、StringBuffer はスレッド セーフです。というのは、これは昨夜暗記したばかりの「Java 面接の質問完全版」に書いてあったことだからです。この時点で、「スレッド セーフとは何ですか?」と尋ね続けるとします。スレッドセーフと非スレッドセーフの違いは何ですか?どのような状況で使用されますか?そんな疑問が次々と湧き出てきました…
非スレッドセーフ現象シミュレーション
ここではArrayListとVectorを使って説明します。
次のコードは、メイン スレッドで新しい非スレッド セーフ ArrayList を作成し、次に 1000 個のスレッドを開いてこの ArrayList に要素をそれぞれ追加します。すべてのスレッドが完了した後、この ArrayList のサイズは次のようになります。それはありますか? 10万あればいいのでは?
public class Main { public static void main(String[] args) { // 进行10次测试 for(int i = 0; i < 10; i++) { test(); } } public static void test() { // 用来测试的List List<Object> list = new ArrayList<Object>(); // 线程数量(1000) int threadCount = 1000; // 用来让主线程等待threadCount个子线程执行完毕 CountDownLatch countDownLatch = new CountDownLatch(threadCount); // 启动threadCount个子线程 for(int i = 0; i < threadCount; i++) { Thread thread = new Thread(new MyThread(list, countDownLatch)); thread.start(); } try { // 主线程等待所有子线程执行完成,再向下执行 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // List的size System.out.println(list.size()); } } class MyThread implements Runnable { private List<Object> list; private CountDownLatch countDownLatch; public MyThread(List<Object> list, CountDownLatch countDownLatch) { this.list = list; this.countDownLatch = countDownLatch; } public void run() { // 每个线程向List中添加100个元素 for(int i = 0; i < 100; i++) { list.add(new Object()); } // 完成一个子线程 countDownLatch.countDown(); } }
上記は 10 回テストされました (なぜ 10 回テストするのでしょうか? スレッド以外の安全性が毎回問題を引き起こすわけではないからです)。
出力結果:
99946 100000 100000 100000 99998 99959 100000 99975 100000 99996
上記の出力結果は、すべてのテスト結果が 100000 であるわけではないことを示しています。 ArrayList の最終サイズが 100000 未満であるテストがいくつかあり、IndexOutOfBoundsException がスローされることもあります。時々。 (この現象が発生しない場合は、何度か試してみてください)
これは、スレッドの安全性以外に起因する問題です。上記のコードを実稼働環境で使用すると、隠れた危険性やバグが存在する可能性があります。
次に、スレッドセーフ Vector を使用してテストします。上記のコードの test() メソッドで
List<Object> list = new ArrayList<Object>();
List<Object> list = new Vector<Object>();
100000 100000 100000 100000 100000 100000 100000 100000 100000 100000
さらに数回実行したところ、問題なくすべて100000であることがわかりました。 Vector はスレッドセーフであるため、複数のスレッドが同じ Vector オブジェクト上で動作しても問題はありません。
非スレッドセーフ! = 安全ではありません
スレッドセーフの実装
public class Main { public static void main(String[] args) { // 进行10次测试 for(int i = 0; i < 10; i++) { test(); } } public static void test() { // 计数器 Counter counter = new Counter(); // 线程数量(1000) int threadCount = 1000; // 用来让主线程等待threadCount个子线程执行完毕 CountDownLatch countDownLatch = new CountDownLatch(threadCount); // 启动threadCount个子线程 for(int i = 0; i < threadCount; i++) { Thread thread = new Thread(new MyThread(counter, countDownLatch)); thread.start(); } try { // 主线程等待所有子线程执行完成,再向下执行 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // 计数器的值 System.out.println(counter.getCount()); } } class MyThread implements Runnable { private Counter counter; private CountDownLatch countDownLatch; public MyThread(Counter counter, CountDownLatch countDownLatch) { this.counter = counter; this.countDownLatch = countDownLatch; } public void run() { // 每个线程向Counter中进行10000次累加 for(int i = 0; i < 10000; i++) { counter.addCount(); } // 完成一个子线程 countDownLatch.countDown(); } } class Counter { private int count = 0; public int getCount() { return count; } public void addCount() { count++; } }
9963727 9973178 9999577 9987650 9988734 9988665 9987820 9990847 9992305 9972233
class Counter { private int count = 0; public int getCount() { return count; } public synchronized void addCount() { count++; } }
10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000
まとめ
以上がJava のスレッド セーフと非スレッド セーフの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。