一芝薇、實例變量+復合操作(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
打印出來的日志是這種執(zhí)行順序
沒有打印出來的是這種執(zhí)行順序
二、成員變量+復合操作(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日志俱饿。
單例模式創(chuàng)建了兩個對象阀捅。原因與上一個例子一樣陨溅。
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、實例變量不正確發(fā)布+不同的鎖
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++;
}
這個是為了提升性能纵竖,帶來的指令重排漠烧。工作內存的數據沒有即使同步到主存中。