【问题标题】:When task is submitted to threads in a thread pool via executor service how to ensure that only 1 thread can occupy a synchronized task at a time?当任务通过执行器服务提交给线程池中的线程时,如何保证一次只能有1个线程占用一个同步任务?
【发布时间】:2021-04-11 08:34:23
【问题描述】:

我有以下工人阶级 -

public class Worker implements Runnable {
    private int workId;

    public Worker(int workId) {
        this.workId = workId;
    }

    private int count = 0;

    private synchronized void increment() {
        System.out.println("Work id: " + workId);
        System.out.println("Incrementing...");
        Thread.sleep(5000);
        count++;
        System.out.println("Incremented");
    }

    @Override
    public void run() {
        increment();
    }
}

我有以下主要方法-

ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 1; i <= 10; i++) {
            executorService.submit(new Worker(i));
        }
        executorService.shutdown();
        System.out.println("All tasks submitted");
        executorService.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("All tasks completed");

此处increment() 已同步。因此,当 1 个线程占用它时,另一个线程必须等到该线程离开锁。

但是当我使用 2 个线程的线程池提交工作时,两个线程似乎同时使用 increment()

那么在这里我怎样才能强制两个线程一次只使用一个increment() 方法呢?

【问题讨论】:

  • 最后你想要什么?现在你有 10 个Worker 对象,有 10 个单独的计数器。
  • 我希望当 1 个工作人员正在处理同步任务时,该工作人员必须在该任务上拥有一个排他锁,并且在第一个工作人员完成任务并释放锁之前,另一个工作人员不能执行相同的任务.
  • 您在工作对象 (this) 上进行同步。如果您有多个工作对象,那么它们确实可以并行运行。

标签: java multithreading threadpool executorservice synchronized


【解决方案1】:
private synchronized void increment() 

此方法锁定在Object 级别起作用,因此如果您有两个对象,它们在调用此方法时不会相互阻塞,因为每个对象都会调用自己的 increment() 方法(同一个Worker 实例上没有并发调用)。


为了避免不同的实例同时访问increment() 方法,您需要在Class 级别进行同步,这是在所有Worker 实例的锁相同时实现的。声明锁的一些选项:

  • 共享Object

    public class Boss extends RichDad implements EarnLotsOfMoney
    {
       private final Object masterLock;
       public Boss() 
       {
          masterLock = new Object();
       }
       public Worker createWorker(int slaveId) 
       {
          return new Worker(masterLock, slaveId);
       }
       //...
    }
    

    是的,我知道的愚蠢的例子..

    public class Worker implements Runnable 
    {
       private final Object lock;
       private int workId;
    
       public Worker(Object lock, int workId) 
       {
          this.lock = lock;
          this.workId = workId;
       }
    
       private void increment() 
       {
           synchronized(lock) /*lock holds the same reference in all instances*/
           {
               //...
           }
       }
    
       @Override
       public void run() {
           increment();
       }
    }
    

lock 只创建一次,然后在创建Worker 实例时作为参数传递。这将阻止所有Worker 实例从同一个 Boss 创建(在这种方法中lock 是一个非静态对象)。


  • 自己的Class

    public class Worker implements Runnable 
    {
       private int workId;
       public Worker(int workId) {
          this.workId = workId;
       }
    
       private int count = 0;
    
       private void increment() 
       {
           synchronized(Worker.class) /*class level lock here*/
           {
             System.out.println("Work id: " + workId);
             System.out.println("Incrementing...");
             Thread.sleep(5000);
             count++;
             System.out.println("Incremented");
           }
       }
    
       @Override
       public void run() {
           increment();
       }
    }
    

这将使用共享的Worker 作为锁来同步线程。

【讨论】:

  • 谢谢,是否可以创建两个Worker.class锁?就像我们创建对象锁一样?对象锁1 =新对象();对象锁2 = new Object();
  • 现在概念非常清楚了,非常感谢
  • @PayelSenapati 我关于锁定对象的最后一个假设并不完全正确,因为非静态对象也可以执行类级别的锁定;我包含了一个示例,基于将对象的相同实例共享给实例构造函数。希望它也有帮助!
猜你喜欢
  • 2013-04-12
  • 1970-01-01
  • 2012-09-03
  • 2016-06-24
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 2012-10-29
  • 2015-07-13
相关资源
最近更新 更多