阅读《Java高并发编程详解》后的笔记。
同步方式提交业务请求:
缺陷:
异步方式提交请求:
客户端提交event后得到一个相应的工单并且立即返回,Event则会被放置在Event队列中。服务端有若干工作线程,不断从event队列中获取任务并且进行异步处理,最后将处理结果保存在一个结果集中,若是客户端想要活的处理结果,可根据工单号再次查询。
相比同步阻塞方式:
缺陷:客户端想要得到结果还需要再次调用接口方法进行查询(可利用异步回调接口的方式解决--待续)。
这两个方法都是Object方法,JDK中每一个类都有这两个方法。
wait方法:
notify方法:
注意事项:
在虚拟机的规范中存在一个wait set(线程休息室)的概念,具体数据结构没有明确定义。线程调用了某个对象的wait方法之后都会被划入到该对象monitor关联的wait set中,并释放monitor的所有权。
wait与sleep区别:
同:(1)都会使得线程进入Blocked
(2)都是可中断方法,中断后都会收到异常。
异:(1)wait方法是Object的,sleep方式是Thread的。
(2)wait方法必须在同步方法中执行,sleep不需要。
(3)线程在同步方法中执行wait方法,会释放monitor的锁,sleep不会。
(4)wait方法在没有指定时间时,被其他线程中断才可以退出阻塞,sleep方法短暂休眠之后自动退出阻塞。
notify每次只能唤醒由于调用了wait方法而阻塞的线程中一个线程,而notifyAll方法可以同时唤醒全部的阻塞线程,同样被唤醒的线程需要继续争抢monitor 的锁。
生产者和消费者是多线程间通信的最好的例子:
import java.util.LinkedList;
import static java.lang.Thread.currentThread;
public class EventQueue {
private final int max;
static class Event{}
private final LinkedList eventQueue = new LinkedList();
private final static int DEFAULT_MAX_EVENT = 10;
public EventQueue(){
this(DEFAULT_MAX_EVENT);
}
public EventQueue(int max){
this.max = max;
}
public void offer(Event event){
synchronized (eventQueue){
while(eventQueue.size() > max){
try {
System.out.println(currentThread().getName()+" the queue is full");
eventQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(currentThread().getName()+" the new event is submitted");
eventQueue.addLast(event);
eventQueue.notifyAll();
}
}
public Event take(){
synchronized (eventQueue){
while(eventQueue.isEmpty()){
try {
System.out.println(currentThread().getName()+" the queue is empty");
eventQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Event event = eventQueue.removeFirst();
this.eventQueue.notifyAll();
System.out.println(currentThread().getName()+" the event"+event+"is handled");
return event;
}
}
}
synchronized关键字提供了一种排他式的数据同步机制,某个线程在获取monitor lock的时候可能会被阻塞。这种阻塞有两个明显的缺陷:无法控制阻塞时长,阻塞不可中断。
自定义一个BooleanLock,具备synchronized关键字所有功能的同时又具备可中断和lock超时的功能:
import java.util.List;
import java.util.concurrent.TimeoutException;
/**
* Lock接口
*/
public interface Lock {
//永远阻塞,除非获取到了锁,可被中断
void lock() throws InterruptedException;
//可被中断,增加超时
void lock(long mills) throws InterruptedException, TimeoutException;
//释放锁
void unlock();
//获取当前有哪些线程被阻塞
List getBlockedThreads();
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeoutException;
import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.currentThread;
public class BooleanLock implements Lock {
//当前拥有锁的线程
private Thread currentThread;
//false代表当前没有任何线程获得锁或者该锁已被释放,true代表currentThread获取了锁
private boolean locked = false;
//存储哪些线程在获取当前线程时进入阻塞状态
private final List blockedList = new ArrayList();
@Override
public void lock() throws InterruptedException {
synchronized (this){
while(locked){
//暂存当前线程
final Thread tempThread = currentThread();
try {
//如果当前锁已经被某个线程获得,则将该线程加入阻塞队列,并使当前线程wait释放对this monitor的所有权
if(!blockedList.contains(tempThread))
blockedList.add(tempThread);
this.wait();
} catch (InterruptedException e) {
//如果当前线程在wait时被打断,从blockedList中将其移除,避免内存泄露
blockedList.remove(tempThread);
//继续抛出中断异常
throw e;
}
}
//如果当前锁没有被其他线程获得,从阻塞队列中删除自己(没有进入阻塞队列删除不会有影响)
blockedList.remove(currentThread());
this.locked = true;
//记录获取锁的线程
this.currentThread = currentThread();
}
}
@Override
public void lock(long mills) throws InterruptedException, TimeoutException {
synchronized (this){
//mills如果不合法,调用lock(),抛出参数非法异常
if(mills <= 0){
this.lock();
}else{
long remainingMills = mills;
long endMills = currentTimeMillis() + remainingMills;
while(locked){
if(remainingMills <= 0)
//说明当前线程是被其他线程唤醒或者在指定的wait时间到了还没有获取锁,超时
throw new TimeoutException("can not get the lock during" + mills + "ms");
if(!blockedList.contains(currentThread()))
blockedList.add(currentThread());
this.wait(remainingMills);
//重新计算remainingMills时间
remainingMills = endMills - currentTimeMillis();
}
//获得锁,并从block列表中删除该线程,指定获得该锁的线程就是当前线程
blockedList.remove(currentThread());
this.locked = true;
this.currentThread = currentThread();
}
}
}
@Override
public void unlock() {
synchronized (this){
//判断当前线程是否是获得锁的那个线程,只有加了锁的线程才有资格解锁
if(currentThread == currentThread()){
this.locked = false;
Optional.of(currentThread().getName() + " release the lock.").ifPresent(System.out::println);
//通知wait set中的线程,可以再次尝试抢锁
this.notifyAll();
}
}
}
@Override
public List getBlockedThreads() {
return Collections.unmodifiableList(blockedList);
}
}
测试一:多个线程通过lock()争抢锁
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;
public class BooleanLockTest {
//定义BooleanLock
private final Lock lock = new BooleanLock();
/**
* try..finally 确保lock每次都能被正确释放
*/
public void synMethod(){
try {
//加锁
lock.lock();
int randomInt = current().nextInt(10);
System.out.println(currentThread() + "get the lock");
TimeUnit.SECONDS.sleep(randomInt);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
public static void main(String args[]){
BooleanLockTest blt = new BooleanLockTest();
//定义一个线程并启动
IntStream.range(0,10).mapToObj(i->new Thread(blt::synMethod)).forEach(Thread::start);
}
}
测试二:可中断被阻塞的线程
public class BooleanLockTest {
//定义BooleanLock
private final Lock lock = new BooleanLock();
/**
* try..finally 确保lock每次都能被正确释放
*/
public void synMethod(){
try {
//加锁
lock.lock();
int randomInt = current().nextInt(10);
System.out.println(currentThread() + "get the lock");
TimeUnit.SECONDS.sleep(randomInt);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
public static void main(String args[]) throws InterruptedException{
BooleanLockTest blt = new BooleanLockTest();
new Thread(blt::synMethod,"T1").start();
TimeUnit.MILLISECONDS.sleep(2);
Thread t2 = new Thread(blt::synMethod,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);
t2.interrupt();
}
}
测试三:阻塞的线程可超时
public class BooleanLockTest {
//定义BooleanLock
private final Lock lock = new BooleanLock();
/**
* try..finally 确保lock每次都能被正确释放
*/
public void synMethod(){
try {
//加锁
lock.lock();
int randomInt = current().nextInt(10);
System.out.println(currentThread() + "get the lock");
TimeUnit.SECONDS.sleep(randomInt);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
public void syncMethodTimeoutable(){
try {
lock.lock(1000);
System.out.println(currentThread() + "get the lock");
int randomInt = current().nextInt(10);
TimeUnit.SECONDS.sleep(randomInt);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String args[]) throws InterruptedException{
BooleanLockTest blt = new BooleanLockTest();
new Thread(blt::synMethod,"T1").start();
TimeUnit.MILLISECONDS.sleep(2);
Thread t2 = new Thread(blt::syncMethodTimeoutable,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);
}
}