Warm tip: This article is reproduced from serverfault.com, please click

Showing usage of Synchronization with 2 Threads in Java

发布于 2020-12-06 09:06:19

I've been looking about multi threading tutorials and those specific about synchronization, but I haven't been able to implement something that I need.

It's a showcase of a synchronization happening in my program.

Basically, I have one class that inherits some functions from other class, those functions needs to be synchronized in order for both of threads doesn't modify an object at the same time (no data corruption).

I previously implemented the code without the synchronize keyword, so I could manage to see the data corruption occurring.

 EditOptionsMethods e1;
    int threadNo;

    public EditOptions() {
        e1 = new BuildAuto();
        run();
    }

    public void run() {
        System.out.println("Thread running.");

        switch (threadNo) {
            case 0:

                break;

            case 1:
                break;
        }

    }

    public void setOptions(String optSetName1, String desiredOption1, String optSetName2, String desiredOption2) {
        e1.s_Option(optSetName1, desiredOption1); // 
        e1.s_Option(optSetName2, desiredOption2);
    }

s_Option will have to be synchronized so both threads won't occur. I firstly will have it without synchronize, then I can initialize a loop (with high index amount, lets say 1000 and then I add with first thread, and subtract with second thread) to see the corruption occurring as an example.

But I don't find a way of showing this.

If someone has an idea of how I could possibly implement this, that'd be awesome.

Questioner
Renan Hiramatsu
Viewed
0
dreamcrash 2020-12-06 18:11:35

First create a structure that will hold the threads:

   List<Thread> threads = new ArrayList<>();

then initialize it, and assign work to the threads:

   int total_threads = 2;
   for(int i = 0; i < total_threads; i++){
       Thread thread  = new Thread(() -> {
           // the work to be done by the threads
       });
       threads.add(thread);
   }

Run the threads:

threads.forEach(Thread::start);

Finally, wait for the threads to finish their work:

   threads.forEach(t -> {
       try {
                t.join();
       } catch (InterruptedException e) {
                e.printStackTrace();
        }
   });

With a running example:

import java.util.*;

public class SomeClass {

    private int x;

    public void addX(int y){ x = x + y;}


    public static void main(String[] args) {
           SomeClass example = new SomeClass();
           List<Thread> threads = new ArrayList<>();
           int total_threads = 2;
           for(int i = 1; i < total_threads + 1; i++){
               final int threadID = i;
               Thread thread  = new Thread(() -> {
                   for(int j = 0; j < 1000; j++ )
                    example.addX((threadID % 2 == 0) ? -threadID : threadID);
               });
               threads.add(thread);
           }
           threads.forEach(Thread::start);
           threads.forEach(t -> {
               try {
                        t.join();
               } catch (InterruptedException e) {
                        e.printStackTrace();
                }
           });
           System.out.println(example.x);
    }
}

Now try to run the example above with and without synchronizing the work done by the threads:

   Thread thread  = new Thread(() -> {
   for(int j = 0; j < 1000; j++ )
       example.addX((threadID % 2 == 0) ? -threadID : threadID);
   });

versus:

 Thread thread  = new Thread(() -> {
 synchronized(example){ // <-- adding synchronization 
    for(int j = 0; j < 1000; j++ )
      example.addX((threadID % 2 == 0) ? -threadID : threadID);
   }
 });

With the version that uses synchronization, you can run the code as many times as you want the output will always be -1000 (using 2 threads). However, with the version without the synchronization the output will be non-deterministic due to the race condition happening during the updates of the variable x.

Instead of using the Thread class directly, you can opt for a higher abstraction, namely executors:

public class SomeClass2 {

  private int x;

  public void addX(int y){x = x + y;}

  public static void main(String[] args) {
      SomeClass2 example = new SomeClass2();
      int total_threads = 2;
      ExecutorService pool = Executors.newFixedThreadPool(total_threads);
      pool.execute(() -> {
         synchronized (example) {
            parallel_task(example, -2);
        }
      });
      pool.execute(() -> {
        synchronized (example) {
            parallel_task(example, 1);
         }
      });

      pool.shutdown();
      try { pool.awaitTermination(1, TimeUnit.MINUTES);} 
      catch (InterruptedException e) {e.printStackTrace();}  
      System.out.println(example.x);
}

private static void parallel_task(SomeClass2 example, int i) {
    thread_sleep();
    for (int j = 0; j < 1000; j++)
        example.addX(i);
}

private static void thread_sleep() {
    try { Thread.sleep(1000); } 
    catch (InterruptedException e) {
        e.printStackTrace();
    }
 }
}

I have added the thread_sleep() to ensure that both parallel tasks are not pick up by the same thread from the thread pool.