ダーティリードを書くいくつかの方法:
1.最初にIncrementAndGetを実行し、次に取得します
package com.demo.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
int i = 0;
ExecutorService executorService = Executors.newFixedThreadPool(3);
while (i < 10) {
TA tt = new TA(atomicInteger);
executorService.execute(tt);
i++;
}
}
}
class TA implements Runnable {
AtomicInteger atomicInteger;
long start = System.currentTimeMillis();
public TA(AtomicInteger atomicInteger) {
this.atomicInteger = atomicInteger;
}
@Override
public void run() {
atomicInteger.incrementAndGet();
try {
//模拟程序执行耗时
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.get());
}
}
2.最初にincrementAndGet、次にgetを割り当て、次にを使用します。
インターネット上の一部の人々は、それを使用する前に値を割り当てれば、あなたは汚い読書をすることはないだろうと言います。それがそうなることがわかります。
int cnt = atomicInteger.get();
System.out.println(cnt);
3.スレッドでは、while(true)はincrementAndGetを自動的にインクリメントし、ロジックに時間がかかり、その後get
package com.demo.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
int i = 0;
ExecutorService executorService = Executors.newFixedThreadPool(3);
while (i < 10) {
TA tt = new TA(atomicInteger);
executorService.execute(tt);
i++;
}
}
}
class TA implements Runnable {
AtomicInteger atomicInteger;
long start = System.currentTimeMillis();
public TA(AtomicInteger atomicInteger) {
this.atomicInteger = atomicInteger;
}
@Override
public void run() {
while(System.currentTimeMillis() -start<20) {
atomicInteger.incrementAndGet();
try {
//模拟程序执行耗时
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( atomicInteger.get());
}
}
}
4. while(true)をスレッド化し、incrementAndGetを最初に実行し、すぐに取得してから、ロジックに時間がかかる
package com.demo.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
int i = 0;
ExecutorService executorService = Executors.newFixedThreadPool(3);
while (i < 10) {
TA tt = new TA(atomicInteger);
executorService.execute(tt);
i++;
}
}
}
class TA implements Runnable {
AtomicInteger atomicInteger;
long start = System.currentTimeMillis();
public TA(AtomicInteger atomicInteger) {
this.atomicInteger = atomicInteger;
}
@Override
public void run() {
while(System.currentTimeMillis() -start<30) {
atomicInteger.incrementAndGet();
int cnt = atomicInteger.get();
try {
//模拟程序执行耗时
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( cnt);
}
}
}
一度実行すると、ダーティリードの可能性が低くなりますが、さらに数回実行してもダーティリードが発生します。しかし、確率ははるかに小さく、約10回の実行と1回の繰り返しです。
ダーティリードに見えない書き込み方法を共有する:
package com.demo.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) {
int i = 0;
ExecutorService executorService = Executors.newFixedThreadPool(3);
while (i < 3) {//3改成100效果一样,还是不会出现脏读,3其实相当于Thread,没用到线程池的重复利用。
TA tt = new TA(atomicInteger);
executorService.execute(tt);
i++;
}
}
}
class TA implements Runnable {
AtomicInteger atomicInteger;
long start = System.currentTimeMillis();
public TA(AtomicInteger atomicInteger) {
this.atomicInteger = atomicInteger;
}
private int cnt;//线程自己的变量
@Override
public void run() {
while(System.currentTimeMillis() -start<3000) {
cnt = atomicInteger.incrementAndGet();
try {
//模拟程序执行耗时
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( cnt);
}
}
}
総括する
1.スレッドプールにタスクを追加するときは、新しいスレッドをループに配置する必要があります。そうしないと、スレッド内のメンバー変数がプログラミングに対して安全でなくなり、ローカル変数を使用することになります。
2.タスクを追加するために新しいスレッドがループに配置され、メンバー変数をスレッドで使用できます。
3.インクリメントアンドゲット()を使用して値を取得します。getは使用しないでください。
4.インクリメントアンドゲットは重複がないことを保証でき、インクリメントが完了した直後に取得された場合でも、取得は重複します。