一、实例变量+复合操作(read-set-write)
package com.tinygao.thread.unsafe;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* Created by gsd on 2017/2/5.
*/
@Slf4j
public class Counter {
public static int count = 0;
public static void inc() {
count++;
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(1000);
for(int i = 0; i < 10000000; i++) {
es.submit(Counter::inc);
}
es.shutdown();
es.awaitTermination(1, TimeUnit.DAYS);
log.info("count:{}", count);
}
}
count值不等于10000000
二、成员变量+复合操作(if-then)
package com.tinygao.thread.unsafe;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
import static java.lang.Thread.sleep;
import static sun.swing.SwingUtilities2.submit;
/**
* Created by gsd on 2017/2/4.
*/
@Slf4j
public class SingletonLazyInit {
private static SingletonLazyInit instance = null;
public static SingletonLazyInit getInstance() throws InterruptedException {
if(instance == null) {
//TimeUnit.NANOSECONDS.sleep(1);
instance = new SingletonLazyInit();
}
return instance;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(2);
for(int i = 0; i < 200; i++) {
Future<SingletonLazyInit> obj1 = es.submit(()->{
return SingletonLazyInit.getInstance();
});
Future<SingletonLazyInit> obj2 = es.submit(()->{
return SingletonLazyInit.getInstance();
});
try {
Preconditions.checkState(obj1.get() == obj2.get(), "error ----> obj1:%s, ojb2:%s",
obj1.get().hashCode(),
obj2.get().hashCode());
} catch (IllegalStateException ex) {
log.error(ex.getMessage());
}
/*log.info("obj1:{}, ojb2:{}",
obj1.get().hashCode(),
obj2.get().hashCode());*/
instance = null;
}
es.shutdown();
}
}
总有概率性的输出error日志。
单例模式创建了两个对象。原因与上一个例子一样。
3、实例变量
package com.tinygao.thread.unsafe;
import com.google.common.base.Preconditions;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by gsd on 2017/2/4.
*/
@Slf4j
public class SetValue {
@Getter
private String value = "";
public void setValue(String value) {
this.value = value;
}
public static void main(String[] args) {
SetValue sv = new SetValue();
ExecutorService es = Executors.newFixedThreadPool(1000);
for(int i = 0; i < 1000000; i++) {
es.submit(()->{
String expect = Thread.currentThread().getName();
sv.setValue(Thread.currentThread().getName());
String result = sv.getValue();
try {
Preconditions.checkState(result.equals(expect),
"expect:%s, result:%s",
expect,
result);
} catch (IllegalStateException e) {
log.error(e.getMessage());
}
});
}
es.shutdown();
}
}
4、线程安全类+复合操作(if-then)也会不安全哦
package com.tinygao.thread.unsafe;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.*;
/**
* Created by gsd on 2017/2/4.
*/
@Slf4j
public class ConcurrentHashMapTest {
private static Map<String, String> map = new ConcurrentHashMap<>();
private static int a = 0;
public static void setValue(String key, String value) {
if(!map.containsKey(key)) {
a++;
map.put(key, value);
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(2);
for(int i = 0; i < 1000; i++) {
Future<Void> thread1 = es.submit(()->{
ConcurrentHashMapTest.setValue("key","value1");
return null;
});
Future<Void> thread2 = es.submit(()->{
ConcurrentHashMapTest.setValue("key","value2");
return null;
});
try {
thread2.get();
thread1.get();
Preconditions.checkState(a <= 1, "map : %s",map);
} catch (IllegalStateException ex) {
log.error(ex.getMessage());
}
a = 0;
map = new ConcurrentHashMap<>();
}
es.shutdown();
}
}
即使用了线程安全的类,你也不一定能写出线程安全的代码。
这个例子概率性的a被加了2次。
原因:是调用客户端语句不是原子操作。
5、实例变量不正确发布+不同的锁
package com.tinygao.thread.unsafe;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Created by gsd on 2017/2/4.
*/
public class ListHelper<E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
锁错了,就跟没锁一样。当然上面这个代码没有封装好。
假设new ListHelper() 为a
线程a: a.putifAbsent获得的是a这个对象的锁
线程b: a.putifAbsent获得的是a这个对象的锁
这两个没有问题,他们先同步的,会串行完成,不会造成线程不安全问题。
但。线程c: a.list.put(x)
线程c获得了a.list.put这个方法锁住的是 list对象。
所以线程a和线程c锁的对象不一样,就会有线程安全问题了。
6、神奇的while
package com.tinygao.thread.unsafe;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* Created by gsd on 2017/2/5.
*/
@Slf4j
public class VolatileTest {
public static boolean asleep = false;
public static long num = 0;
public static void service() {
while(!asleep) {
num++;
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(1);
es.submit(VolatileTest::service);
TimeUnit.MILLISECONDS.sleep(100);
asleep = true;
log.info("sleep1 ~~~~~~ num: {}", num);
num = 42;
TimeUnit.SECONDS.sleep(1);
log.info("sleep2 ~~~~~~ num: {}", num);
TimeUnit.SECONDS.sleep(1);
log.info("sleep3 ~~~~~~ num: {}", num);
es.shutdown();
es.awaitTermination(1, TimeUnit.DAYS);
}
}
在代码中,即时主线程设置了asleep=true,但是子线程中的while还是停不下来。
在《effective java中文版》——第二版中p230提到:
while(!done)
i++;
转成这样了:
if(!done) {
while(true)
i++;
}
这个是为了提升性能,带来的指令重排。工作内存的数据没有即使同步到主存中。