Java:这种业务逻辑如何做到线程安全呢?

发布于 2022-09-01 19:32:09 字数 476 浏览 40 评论 0

public class Process implements Runnable {
    public static Set<String> set = new HashSet<String>();

    public void run() {
        // 1、遍历取出set中元素
        // 2、具体业务逻辑
        // 3、清空set
        set.clear();
    }

    public void storage(String str) {
        set.add(str);
    }
}

其他模块会调用storage()向Set中添加元素,而Process会定时取出并处理Set中的元素,如何保证在Process的run()执行的同时,其他模块不会向Set中添加元素呢?如果在步骤2、3之间刚有其他模块向Set中添加元素,这个元素还没有得到处理就被消除掉了,希望得到大家的指点。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

感性不性感 2022-09-08 19:32:09

首先解决你的问题:

public class Process implements Runnable {
    public static Set<String> set = new HashSet<String>();

    public void run() {
        synchronized (set) {
            // 1、遍历取出set中元素
            // 2、具体业务逻辑
            // 3、清空set
            set.clear();
        }
    }

    public void storage(String str) {
        synchronized (set) {
            set.add(str);
        }
    }
}

在以上两个地方加上synchronized (set),由于set是static的,所以只有一个实例,被锁住的代码块可以保证同时只有一个线程能运行进去。也就是在【遍历取出set中元素->具体业务逻辑->清空set】的同时,storage方法中的set.add(str)是要阻塞等待上面的操作执行完成才能add。

需要注意的是,不能使用synchronized (this),因为Process可能会有多个实例(其实根据你给的这些代码我也判断不出是否有多个实例,猜的..)。

最后给你建议一个性能更好的解决方法:使用阻塞队列ArrayBlockingQueue,因为你的问题是一个典型的生产者消费者模型,而且不需要你自己考虑线程安全问题,阻塞队列本身已经帮你解决了。你的现有的程序中,如果消费者(也就是run方法)正在处理任务,生产者(即storage方法)是不能往队列里面添加任务的。而如果使用ArrayBlockingQueue可以一边生产一边消费。

DEMO:

public class Process implements Runnable {
    
    // 使用阻塞队列,队列的默认大小10,可以根据需求调整
    public static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);

    public void run() {
        try {
            String task = queue.take(); // 线程安全的方式取一条任务,如果队列为空则阻塞等待直到有新的任务加入进来
            process(task); // 处理任务,具体逻辑
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void storage(String str) {
        try {
            queue.put(str); // 线程安全的方式加一条任务,如果队列满了则阻塞等待直到消费者取走任务有空间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

当然以上代码还可以改的模块更清晰可读性更高一点,这里我就不再继续写了。

最后,有关线程安全和线程同步,我写过几篇博客,这里安利一下:
Java线程同步:synchronized锁住的是代码还是对象:http://xxgblog.com/2012/11/15/java-synchronized/
Java线程安全和非线程安全:http://xxgblog.com/2013/05/16/java-thread-safe/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文