Статьи

Параллелизм Java с ReadWriteLock

Написание многопоточных Java-приложений — это не просто. Необходимо проявлять особую осторожность, потому что плохая синхронизация может поставить ваше приложение на колени. Куча JVM является общей для всех потоков. Если нескольким потокам необходимо одновременно использовать одни и те же объекты или переменные статического класса, доступ к общим данным для потоков должен тщательно контролироваться. Начиная с версии 1.5, в JSDK включены служебные классы, обычно полезные для параллельного программирования.

В Java ключевое слово для синхронизации используется для получения эксклюзивной блокировки на объекте. Когда поток получает блокировку объекта либо для чтения, либо для записи, другие потоки должны дождаться снятия блокировки этого объекта. Представьте себе, что существует множество потоков чтения, которые часто читают общие данные, и только один поток записи, который обновляет общие данные. Нет необходимости блокировать доступ исключительно к совместно используемым данным во время чтения, потому что несколько операций чтения могут выполняться параллельно, если нет операции записи.

В этом посте я приведу пример использования интерфейса ReadWriteLock, который представлен в Java 1.5 API Doc. В документации Java Api говорится:

ReadWriteLock поддерживает пару связанных блокировок,
один для операций только для чтения и один для записи.
Блокировка чтения может удерживаться одновременно несколькими нитями чтения,
до тех пор, пока нет писателей. Блокировка записи является эксклюзивной.

Читатель потоков может читать общие данные одновременно. Операция чтения не блокирует другие операции чтения. Это тот случай, когда вы выполняете оператор SQL SELECT. Но операция записи является эксклюзивной. Это означает, что все читатели и другие писатели блокируются, когда поток писателя удерживает блокировку для изменения общих данных.

Writer.java Этот класс представляет поток, который обновляет общие данные. Writer использует WriteLock из ReadWriteLock для исключительной блокировки доступа к словарю.

01
02
03
04
05
06
07
08
09
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
package deneme.readwritelock;
  
  
 public class Writer extends Thread{
   private boolean runForestRun = true;
   private Dictionary dictionary = null;
    
   public Writer(Dictionary d, String threadName) {
     this.dictionary = d;
     this.setName(threadName);
   }
   @Override
   public void run() {
     while (this.runForestRun) {
       String [] keys = dictionary.getKeys();
       for (String key : keys) {
         String newValue = getNewValueFromDatastore(key);
         //updating dictionary with WRITE LOCK
         dictionary.set(key, newValue);
       }
        
       //update every seconds
       try {
         Thread.sleep(1000);
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
     }
   }
   public void stopWriter(){
     this.runForestRun = false;
     this.interrupt();
   }
   public String getNewValueFromDatastore(String key){
     //This part is not implemented. Out of scope of this artile
     return "newValue";
   }
 }

Reader.java Этот класс представляет поток, который читает общие данные.

01
02
03
04
05
06
07
08
09
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
package deneme.readwritelock;
  
 public class Reader extends Thread{
    
   private Dictionary dictionary = null;
   public Reader(Dictionary d, String threadName) {
     this.dictionary = d;
     this.setName(threadName);
   }
    
   private boolean runForestRun = true;
   @Override
   public void run() {
     while (runForestRun) {
       String [] keys = dictionary.getKeys();
       for (String key : keys) {
         //reading from dictionary with READ LOCK
         String value = dictionary.get(key);
          
         //make what ever you want with the value.
         System.out.println(key + " : " + value);
       }
        
       //update every seconds
       try {
         Thread.sleep(1000);
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
     }
   }
    
   public void stopReader(){
     this.runForestRun = false;
     this.interrupt();
   }
 }

Dictionary.java Это простой и многопоточный словарь. Операции чтения управляются через ReadLock, а операции записи (обновления) — через WriteLock.

01
02
03
04
05
06
07
08
09
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
package deneme.readwritelock;
  
 import java.util.HashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
  
 public class Dictionary {
    
   private final ReentrantReadWriteLock readWriteLock =
     new ReentrantReadWriteLock();
  
   private final Lock read  = readWriteLock.readLock();
    
   private final Lock write = readWriteLock.writeLock();
    
   private HashMap<String, String> dictionary = new HashMap<String, String>();
    
   public void set(String key, String value) {
     write.lock();
     try {
       dictionary.put(key, value);
     } finally {
       write.unlock();
     }
   }
    
   public String get(String key) {
     read.lock();
     try{
       return dictionary.get(key);
     } finally {
       read.unlock();
     }
   }
  
   public String[] getKeys(){
     read.lock();
     try{
       String keys[] = new String[dictionary.size()];
       return dictionary.keySet().toArray(keys);
     } finally {
       read.unlock();
     }
   }
    
   public static void main(String[] args) {
     Dictionary dictionary = new Dictionary();
     dictionary.set("java""object oriented");
     dictionary.set("linux", "rulez");
     Writer writer  = new Writer(dictionary, "Mr. Writer");
     Reader reader1 = new Reader(dictionary ,"Mrs Reader 1");
     Reader reader2 = new Reader(dictionary ,"Mrs Reader 2");
     Reader reader3 = new Reader(dictionary ,"Mrs Reader 3");
     Reader reader4 = new Reader(dictionary ,"Mrs Reader 4");
     Reader reader5 = new Reader(dictionary ,"Mrs Reader 5");
     writer.start();
     reader1.start();
     reader2.start();
     reader3.start();
     reader4.start();
     reader5.start();
   }
    
 }

Ссылка: Пример ReadWriteLock на Java от нашего партнера по JCG Илькина Уласа на Все ваши базы принадлежат нам в блоге.