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

本文介绍GuardedSuspension模式的实现方式,包括Request、RequestQueue等类的设计,以及ClientThread与ServerThread如何通过同步机制高效地进行请求的发送与处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 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<Request> 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<Request> queue = new LinkedList<>();

    private final BlockingQueue<Request> 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();
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值