Wednesday, September 7, 2016

JAVA MULTITHREADING: Multiple Locks Using Synchronized Code Blocks

So even 'synchronized' methods don't cut it all the time. When writing to a single resource, the aforementioned keyword does it.

The problem usually arises when there are two or more resources to write to synchronously.

Using synchronized methods only leads to racing conditions where one thread is completely halted by the execution of the other thread. This leads to double time taken for 2 resources, triple for 3 and so on.

When one resource is being written to, the other resource is free and should be accessible for R/W! This is what we'll be dealing with in this log.

Running the code below would result in 4-6 seconds depending on your computer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Worker {
 private Random random = new Random();
 
 
 /**
  * Two independent fields can be seen below.
  * Operation on one should not affect the operation on another
  */
 private List<Integer> list1 = new ArrayList<Integer>();
 private List<Integer> list2 = new ArrayList<Integer>();
 
 private synchronized void stageOne() { // <-- You might think this would help
  try{
   Thread.sleep(1);
  }catch(InterruptedException e){
   e.printStackTrace();
  }
  
  list1.add(random.nextInt(100));
 }
 
 private synchronized void stageTwo() {
  try{
   Thread.sleep(1);
  }catch(InterruptedException e){
   e.printStackTrace();
  }
  
  list2.add(random.nextInt(100));
 }
 public void process() {
  for(int i=0; i<1000; i++){
   stageOne();
   stageTwo();
  }
 }
 public void execute(){
  System.out.println("Starting...");
  
  long start = System.currentTimeMillis();
  Thread t1 = new Thread(new Runnable(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    process();
   }
   
  });
  t1.start();
  
  Thread t2 = new Thread(new Runnable(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    process();
   }
   
  });
  t2.start();
  
  try {
   t1.join(); // <-- This is to take control from main() and execute until done
   t2.join();
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  long end = System.currentTimeMillis();
  
  System.out.println("Time taken = " + (end-start));
  System.out.println("List1 size = " + list1.size() + ", List2 size = " + list2.size());
 }
 
 public static void main(String[] args){
  Worker worker = new Worker();
  worker.execute();
 }
}

This time interval is too long because of the bottleneck formed by the use of synchronized methods.




********************************************************************************


Is there a way out, or a way to reduce the time interval?
Hell yes!

A very new concept -- SYNCHRONIZED BLOCKS (NOT FREAKING METHODS LIKE BEFORE)!

Execution time would be half for this one, since there are two resources being simultaneously accessed. Had there been three resources and corresponding number of threads, that is 3 threads, the time taken would be reduced by 3. NOW THAT'S A FREAKING LOT, HOMIE!


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Worker {
 private Random random = new Random();
 
 
 /**
  * Two independent fields can be seen below.
  * Operation on one should not affect the operation on another
  */
 private List<Integer> list1 = new ArrayList<Integer>();
 private List<Integer> list2 = new ArrayList<Integer>();
 
 private void stageOne() { // <-- You might think this would help
  synchronized(list1){
   try{
    Thread.sleep(1);
   }catch(InterruptedException e){
    e.printStackTrace();
   }
   
   list1.add(random.nextInt(100));
  }
  
 }
 
 private void stageTwo() {
  synchronized(list2){
   try{
    Thread.sleep(1);
   }catch(InterruptedException e){
    e.printStackTrace();
   }
   
   list2.add(random.nextInt(100));
  }
  
 }
 public void process() {
  for(int i=0; i<1000; i++){
   stageOne();
   stageTwo();
  }
 }
 public void execute(){
  System.out.println("Starting...");
  
  long start = System.currentTimeMillis();
  Thread t1 = new Thread(new Runnable(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    process();
   }
   
  });
  t1.start();
  
  Thread t2 = new Thread(new Runnable(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    process();
   }
   
  });
  t2.start();
  
  try {
   t1.join(); // <-- This is to take control from main() and execute until done
   t2.join();
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  long end = System.currentTimeMillis();
  
  System.out.println("Time taken = " + (end-start));
  System.out.println("List1 size = " + list1.size() + ", List2 size = " + list2.size());
 }
 
 public static void main(String[] args){
  Worker worker = new Worker();
  worker.execute();
 }
}

No comments:

Post a Comment