多线程基础之设计模式Guarded Suspension模式

一. Guarded Suspension模式

        Guarded 是被守护,被保卫的意思,Suspension 则是 “暂停的意思”,如果执行现在的处理出现问题,就让执行处理的线程进行等待,这就是 Guarded Suspension 模式,它是通过让线程等待保证实例的安全性

二. 示例程序

类名 说明        
Request        表示一个请求类
RequestQueue 依次存放请求的类
ClientThread 发送请求的类
ServerThread 接受请求的类
Main         测试程序

2.1 Request类

        用于表示请求,由于只是用于表示 ClientThread 传递给 ServerThread 的实例,所以不用做特殊处理,该类中有一个表示名称的属性(name字段)

@Getter
@Setter
public class Request {

    private final String name;

    public Request(String name) {
        this.name = name;
    }
}

2.2 RequestQueue类

        该类用于存放请求,定义了 getRequest 和 putRequest 两个方法

        getRequest 方法会取出最先存放再 RequestQueue 中的请求,作为其返回值,如果一个请求都没有,那就一直等待,直到其他某个线程执行 putRequest 方法

        putRequest 方法用于添加一个请求,当线程需要向 RequestQueue 中添加 Request 实例,可以调用该方法

public class RequestQueue {

    private final Queue queue = new LinkedList<>();

    public synchronized Request getRequest() {
        while (queue.peek() == null) {
            try {
                // 队列为空时,当前线程进入等待队列
                wait();
            } catch (InterruptedException e) {
                //
            }
        }
        return queue.remove();
    }

    public synchronized void putRequest(Request request) {
        queue.offer(request);
        // 唤醒,把等待队列中的线程移出去
        notifyAll();
    }
}

2.3 ClientThread类

        ClientThread 类用于表示发送请求的线程,ClientThread 持有 RequestQueue 的实例,并连续调用该实例的 putRequest,放入请求,为了错开发送的请求时间点,使用random类随机生成了0到100的数字,来作为 sleep 的时间(毫秒)

public class ClientThread extends Thread {

    private final Random random;
    private final RequestQueue requestQueue;

    public ClientThread(RequestQueue requestQueue, String name, long seed) {
        super(name);
        this.requestQueue = requestQueue;
        this.random = new Random(seed);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            Request request = new Request("NO." + i);
            System.out.println(Thread.currentThread().getName() + "request" + request);
            // 请求放入队列
            requestQueue.putRequest(request);
            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                //
            }
        }
    }
}

2.4 ServerThread类

        ServerThread 类用于表示接收请求的线程,该类也持有 RequestQueue 的实例,使用 getRequest 方法接收请求,与 ClientThread 一样,ServerThead 也是用随机数进行 sleep

public class ServerThread extends Thread {

    private final Random random;
    private final RequestQueue requestQueue;

    public ServerThread(RequestQueue requestQueue, String name, long seed) {
        super(name);
        this.requestQueue = requestQueue;
        this.random = new Random(seed);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            // 从队列中取出请求
            Request request = requestQueue.getRequest();
            System.out.println(Thread.currentThread().getName() + "request" + request);
            try {
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
                //
            }
        }
    }
}

2.5 Main类

        Main 类首先会创建 RequestQueue 的实例,然后分别创建 ClientThread 和 ServerThread 的实例,并将 requestQueue 传给这两个实例,最后执行 start

public class Main {

    public static void main(String[] args) {
        RequestQueue queue = new RequestQueue();
        new ClientThread(queue, "Alice", 3141592L).start();
        new ServerThread(queue, "bili", 2141592L).start();
    }
}

2.6 运行结果

        运行结果可以看出,名为 Alice 的线程会一直发送请求,而名为 bili 的线程会一直处理请求

Alice, requestRequest(name=NO.0)   ALice 发送请求
bili, handlersRequest(name=NO.0)   bili 处理请求
Alice, requestRequest(name=NO.1)
bili, handlersRequest(name=NO.1)
Alice, requestRequest(name=NO.2)
bili, handlersRequest(name=NO.2)
Alice, requestRequest(name=NO.3)
bili, handlersRequest(name=NO.3)
Alice, requestRequest(name=NO.4)
bili, handlersRequest(name=NO.4)
Alice, requestRequest(name=NO.5)
bili, handlersRequest(name=NO.5)
Alice, requestRequest(name=NO.6)

2.7 wait与notify、notifyAll的责任

        仔细查看程序,你会发现 wait/notify/notifyAll 只会出现在 RequestQueue 类中,这种将wait/notify/notifyAll 隐藏起来的做法对 RequestQueue 类的复用性来说是非常重要的,这是因为,使用 RequestQueue 的其他类无需考虑 wait或notify的问题,只需要调用 getRequest 和 putRequest 就行了

三. 使用LinkedBlockingQueue

        jdk1.5的 java.util.concurrent 包中提供了与该 RequestQueue 类功能相同的一个类,那就是 LinkedBlockingQueue类,采用这个类,可以将 RequestQueue 类简化,这里使用的 take 方法和 put 方法都是 BlockingQueue 接口中声明的方法,take 方法取出队列首位的元素,put 方法则用于向队列末尾添加元素,当队列为空是,若调用 take 方法便会进行 wait,由于 take 方法和 put 方法已经考虑可互斥处理,所以不需要使用 syncronized,能够保证线程安全

        

public class RequestQueue {

    private final Queue queue = new LinkedList<>();

    private final BlockingQueue blockingQueue = new LinkedBlockingDeque<>();

    public Request getRequest() {
        Request request = null;
//        while (queue.peek() == null) {
//            try {
//                // 队列为空时,当前线程进入等待队列
//                wait();
//            } catch (InterruptedException e) {
//                //
//            }
//        }
//        // 获取被删除的第一个元素
//        return queue.remove();
        try {
            // 查询源码会发现,当队列为空时, 会进行 wait
            request = blockingQueue.take();
        } catch (InterruptedException e) {
            //
        }
        return request;
    }

    public void putRequest(Request request) {
        try {
            blockingQueue.put(request);
        } catch (InterruptedException e) {
            //
        }
        // 唤醒,把等待队列中的线程移出去
//        notifyAll();
    }
}

你可能感兴趣的:(多线程学习基础之设计模式,设计模式,学习,java,后端)