0

I'm new with thread pools, and learning to use synchronized

This code has the issue of race condition:

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit ;
public class Counter implements Runnable{
    int count;
    public Counter(){
        count=0;
    }
    public void run(){
        count++;
    }
    public static void main(String[] args) throws 
    InterruptedException{
        ExecutorService exec=Executors.newFixedThreadPool(2);
        Counter task=new Counter();
        for (int i=0;i<1000;i++ ) {
            exec.execute(task); 
        }
        exec.shutdown();
        exec.awaitTermination(50L,TimeUnit.SECONDS);
        System.out.println(task.count);
    }
}

In this code race condition is taken cared off:

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit ;
public class Counter implements Runnable{
    int count;
    public Counter(){
        count=0;
    }
    public synchronized void run(){
        count++;
    }
    public static void main(String[] args) throws 
    InterruptedException{
        ExecutorService exec=Executors.newFixedThreadPool(2);
        Counter task=new Counter();
        for (int i=0;i<1000;i++ ) {
            exec.execute(task); 
        }
        exec.shutdown();
        exec.awaitTermination(50L,TimeUnit.SECONDS);
        System.out.println(task.count);
    }
}

But I think in second implementation the there's no point using threads as the execution will be "sort of" sequential. As only one thread out of the two will have the access of the object monitor while the other one will wait until execution of first thread and will be the monitor's access only when the first one is done. This sounds like sequential.

Please correct me if I'm wrong. Any help is highly appreciated.

  • Yes, using threads is useless here. – Johannes Kuhn Nov 09 '19 at 14:50
  • You may be interested in [AtomicInteger](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html) object and it's `incrementAndGet()` method. – Kirill Nov 09 '19 at 15:10
  • [LongAdder](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html) is superior to [AtomicInteger](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html). https://stackoverflow.com/questions/30691083/how-longadder-performs-better-than-atomiclong – drekbour Nov 09 '19 at 15:27

3 Answers3

1

Yes. The synchronized keyword on non-static methods restricts the given method to sequential execution per instance. You only have one instance of Counter and are reusing it for all tasks, so, even though you have a thread pool with 2 threads, only one will be executing run() at any given time.

andresp
  • 1,624
  • 19
  • 31
0

Making run() method synchronized is kind of voiding the multi-threading. It of course leads to sequential processing

Please see Should you synchronize the run method? Why or why not?

0

First of all, the code provided above is just for learning cases, which makes the learner know the race conditions could happen when using multithread and meanwhile how to take care of race conditions. In the code provided above, it is indeed sequential due to synchronized keyword make only one thread could access the block (critical section). Considering context switching time to be negligible and fair scheduling, the multithread version’s run time will be greater than the time used in one thread version.

But take a step back. The benefit we could get from Multithreading is we could have the execution of multiple parts of a program at the same time. So multithreading leads to maximum utilization of the CPU by multitasking. So actually we need to consider two scenarios. one is CPU heavy programs (eg, the example in your code). The other is the programs that have a lot of I/O or network bound processing (very common in web-applications).

The second one will get interesting since the entire process is not happening in the CPU.

For example, we have

task1 : 2 seconds request some data from I/O + 2 second CPU

task2 : 2 seconds request some data from I/O + 2 second CPU

If we run in one thread, then the total time will be 2s + 2s + 2s + 2s = 8 s. But if we run in two thread, and we assuming thread context switch will cost 1s(the actual time is very quick, we could ignore)

Start -> task1 I/O request at 0

-> task1 block and thread context change to task2 and taks2 begin to make I/O request at 1s

-> task2 block and thread context change to task1 and task1 get all the data from IO at 2s

-> task2 finish CPU operation at 4s

-> thread context change to task2 at 5s

-> task2 finish CPU at 7s.

-> end

Considering 1s context switching time and 3 context-switches(3 s) = 7s. Which is still better than a single-threaded program.

Leogao
  • 294
  • 2
  • 3