前回と同じことをJavaでしてみよう。
long.java
public class Main {
private static final int REPEAT = 500000000;
private static long a = 0;
public static void main(String[] args) throws InterruptedException{
Thread th1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i <REPEAT; i++){
a = 1;
check();
}
}
});
Thread th2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i <REPEAT; i++){
a = -1;
check();
}
}
});
th1.start();
th2.start();
th1.join();
th2.join();
System.out.println("FINISHED!");
}
private static void check(){
if(a != 1 && a != -1){
System.out.println("LONG VALUE HAS BROKEN!");
}
}
}
排他制御ではないが、Javaで同様の問題を解決するにはvolatile修飾子がある
private volatile static long a = 0;
とすれば問題がなくなる。
volatileには「値の参照の際に、常に最新の値を見に行く」という性質があり、
ざっくり言えば、volatileの代入や参照はロックされているような振る舞いをする。
でもこれでは安全と言えないので、AtomicLongを使う。
アトミック: 不可分操作。ある操作を行なうときに他者がその操作に割り込めないことを指す。
private static AtomicLong a = new AtomicLong(0)
Javaでの排他制御には、synchronizedブロックが使える。
synchronized
public class Main {
private static final int REPEAT = 500000000;
private static Long a = new Long(0);
public static void main(String[] args) throws InterruptedException{
Thread th1 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i <REPEAT; i++){
synchronized (a){
a = new Long(1);
check();
}
}
}
});
Thread th2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i <REPEAT; i++){
synchronized (a) {
a = new Long(-1);
check();
}
}
}
});
th1.start();
th2.start();
th1.join();
th2.join();
System.out.println("FINISHED!");
}
private static void check(){
if(a != 1 && a != -1){
System.out.println("LONG VALUE HAS BROKEN!");
}
}
}
Javaで並列処理を行うには、Stream#parallelが使える。
parallel
public class Main {
public static void main(String[] args) {
// write your code here
IntStream.range(1,10).parallel().forEach(System.out::println);
}
}