githubjava
java併發模型是基於內存共享,多線程共享變量通常就會涉及到加鎖,這裏介紹幾種下降鎖競爭的方式,最終效果都是下降鎖的顆粒或者下降鎖的競爭次數。git
減小鎖的持有時間。github
例如對一個方法加鎖不如對其中的同步代碼行加鎖。算法
讀寫鎖。多線程
可只對鎖操做加鎖,讀不加鎖。這樣讀、讀之間不互斥, 讀、寫和寫、讀互斥,可以使用J.U.C中的ReadWriteLock
。併發
減小鎖顆粒。app
如ConcurrentHashMap中對segment加鎖,而不是整個map加鎖。好比map長度爲128,分紅8份,8份之間不互斥,8分內部才互斥,能夠有效下降鎖的競爭。ide
樂觀鎖。優化
使用CAS算法,和期待值對比,若是同樣則執行,不同則重試等方式。this
鎖粗化。
若是一個方法有好幾行都是同步代碼,對這幾行單獨加鎖,不如對這個方法加鎖,能夠減小鎖的競爭次數。
服務A和服務B都會調用服務C的接口poll,同一個服務調用時互斥須要加鎖,不一樣服務之間調用不互斥。 好比A服務有兩個實例A一、A2,B服務有兩個實例B一、B2, A1和B1共同調用poll接口時能夠並行訪問,A1和A2共同訪問時必須串行執行, 即必須有一個請求執行完才能夠執行完下一個。
弱引用做用能夠加快垃圾回收,這裏的服務名不多,因此生成的鎖對象不多能夠不使用弱引用。但是其餘場景可能要生成的鎖對象有不少,可使用弱引用加快垃圾回收。
@Component
public class StringLockProvider {
private final Map<Mutex, WeakReference<Mutex>> mutexMap = new WeakHashMap<>();
public Mutex getMutex(String id) {
if (id == null) {
throw new NullPointerException();
}
Mutex key = new MutexImpl(id);
synchronized (mutexMap) {
return mutexMap.computeIfAbsent(key, WeakReference::new).get();
}
}
public interface Mutex {
}
private static class MutexImpl implements Mutex {
private final String code;
private MutexImpl(String id) {
this.code = id;
}
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this.getClass() == o.getClass()) {
return this.code.equals(o.toString());
}
return false;
}
public int hashCode() {
return code.hashCode();
}
public String toString() {
return code;
}
}
}
@RestController
public class PollController {
@Autowired
private StringLockProvider lockProvider;
/** * @param service 拉取的服務名 */
@GetMapping("/v1/data/{service}")
public List<String> poll(@PathVariable("service") String service) {
List<String> data = new ArrayList<>(2 << 4);
synchronized (lockProvider.getMutex(service)) {
//同步代碼,data.add
}
return data;
}
}
複製代碼
此種效果和
弱引用+synchronized
一致, 此處不一樣主要在於ReentrantLock支持公平鎖, 誰也等待誰先獲取, 而synchronized非公平鎖,隨機選一個獲取鎖,當一個線程一直獲取不到鎖,須要等待較長時間,可能形成該接口超時。
@RestController
public class PollController {
private final Map<String, WeakReference<ReentrantLock>> mutexMap = new ConcurrentHashMap<>();
/** * @param service 拉取的服務名 */
@GetMapping("/v1/data/{service}")
public List<String> poll(@PathVariable("service") String service) {
List<String> data = new ArrayList<>(2 << 4);
ReentrantLock lock = getReentrantLock(service);
lock.lock();
try {
//同步代碼。data.add
} finally {
lock.unlock();
}
return data;
}
private ReentrantLock getReentrantLock(String id) {
if (id == null) {
throw new NullPointerException();
}
return mutexMap.computeIfAbsent(id, it -> new WeakReference<>(new ReentrantLock(true))).get();
}
}
複製代碼
此處使用了while循環,使用不當會形成線程阻塞,阻塞過多可能會形成死機!
@RestController
public class PollController {
private final Map<String, WeakReference<AtomicBoolean>> atomicMap = new ConcurrentHashMap<>();
/** * @param service 拉取的服務名 */
@GetMapping("/v1/data/{service}")
public List<String> poll(@PathVariable("service") String service) {
List<String> data = new ArrayList<>(2 << 4);
AtomicBoolean atomic = getAtomicBoolean(service);
//直到預期的值爲true,纔會成功,不然循環
while (!atomic.compareAndSet(true, false)) {
//Thread.sleep(100)
}
try {
//同步代碼。data.add
}finally {
atomic.set(true);
}
return data;
}
private AtomicBoolean getAtomicBoolean(String id) {
if (id == null) {
throw new NullPointerException();
}
return atomicMap.computeIfAbsent(id, it -> new WeakReference<>(new AtomicBoolean(true))).get();
}
}
複製代碼