简体   繁体   中英

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?

I have following worker class -

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();
    }
}

I have following main method -

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");

Here increment() has been synchronized. So when 1 thread is occupying it another thread has to wait until that thread leaves the lock.

But when I am submitting the work using a thread pool of 2 threads, both threads seem to be using increment() at the same time.

So here how can I force the two threads to use the increment() method only one at a time?

private synchronized void increment() 

This method locking works at the Object level , so if you have two Objects, they won't block each other when calling this, as each one would call its own increment() method (there is no concurrent call on the same Worker instance).


In order to avoid different instances accessing the increment() method concurrently, you need synchronization at Class level, which is achieved when the lock is the same for all of your Worker instances. Some options to declare the lock:

  • Shared 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); } //... }

    yep silly example i know..

     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 is created only once and then is passed as a parameter when creating the Worker instances. This would block all Worker instances created from the same Boss ( in this approach the lock is a non-static object ).


  • Its own 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(); } }

This would synchronize the threads using the shared Worker class as lock.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM