当任务通过执行器服务提交给线程池中的线程时,如何确保一次只有1个线程可以占用一个同步任务?

问题描述

我有以下工人阶级 -

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() 方法

解决方法

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 class 作为锁来同步线程。