2016-01-05 80 views
2

我試圖證明,當有許多讀者和只有一些作家同步較慢。不知何故,我證明了相反。同步與ReadWriteLock性能

的RW例如,執行時間爲313毫秒:

package zad3readWriteLockPerformance; 

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReadWriteLock; 
import java.util.concurrent.locks.ReentrantReadWriteLock; 

public class Main { 
    public static long start, end; 

    public static void main(String[] args) { 
     Runtime.getRuntime().addShutdownHook(new Thread(() -> { 
      end = System.currentTimeMillis(); 
      System.out.println("Time of execution " + (end - start) + " ms"); 
     })); 
     start = System.currentTimeMillis(); 
     final int NUMBER_OF_THREADS = 1000; 
     ThreadSafeArrayList<Integer> threadSafeArrayList = new ThreadSafeArrayList<>(); 
     ArrayList<Thread> consumerThreadList = new ArrayList<Thread>(); 
     for (int i = 0; i < NUMBER_OF_THREADS; i++) { 
      Thread t = new Thread(new Consumer(threadSafeArrayList)); 
      consumerThreadList.add(t); 
      t.start(); 
     } 

     ArrayList<Thread> producerThreadList = new ArrayList<Thread>(); 
     for (int i = 0; i < NUMBER_OF_THREADS/10; i++) { 
      Thread t = new Thread(new Producer(threadSafeArrayList)); 
      producerThreadList.add(t); 
      t.start(); 

     } 



     // System.out.println("Printing the First Element : " + threadSafeArrayList.get(1)); 

    } 

} 
class Consumer implements Runnable { 
    public final static int NUMBER_OF_OPERATIONS = 100; 
    ThreadSafeArrayList<Integer> threadSafeArrayList; 

    public Consumer(ThreadSafeArrayList<Integer> threadSafeArrayList) { 
     this.threadSafeArrayList = threadSafeArrayList; 
    } 

    @Override 
    public void run() { 
     for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { 
      Integer obtainedElement = threadSafeArrayList.getRandomElement(); 
     } 
    } 

} 
class Producer implements Runnable { 
    public final static int NUMBER_OF_OPERATIONS = 100; 
    ThreadSafeArrayList<Integer> threadSafeArrayList; 

    public Producer(ThreadSafeArrayList<Integer> threadSafeArrayList) { 
     this.threadSafeArrayList = threadSafeArrayList; 
    } 

    @Override 
    public void run() { 
     for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { 
      threadSafeArrayList.add((int) (Math.random() * 1000)); 
     } 
    } 

} 

class ThreadSafeArrayList<E> { 
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 

    private final Lock readLock = readWriteLock.readLock(); 

    private final Lock writeLock = readWriteLock.writeLock(); 

    private final List<E> list = new ArrayList<>(); 

    public void add(E o) { 
     writeLock.lock(); 
     try { 
      list.add(o); 
      //System.out.println("Adding element by thread" + Thread.currentThread().getName()); 
     } finally { 
      writeLock.unlock(); 
     } 
    } 

    public E getRandomElement() { 
     readLock.lock(); 
     try { 
      //System.out.println("Printing elements by thread" + Thread.currentThread().getName()); 
      if (size() == 0) { 
       return null; 
      } 
      return list.get((int) (Math.random() * size())); 
     } finally { 
      readLock.unlock(); 
     } 
    } 

    public int size() { 
     return list.size(); 
    } 

} 

同步例如,執行的時間僅爲241ms:

package zad3readWriteLockPerformanceZMIENONENENASYNCHRO; 

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 

public class Main { 
    public static long start, end; 

    public static void main(String[] args) { 
     Runtime.getRuntime().addShutdownHook(new Thread(() -> { 
      end = System.currentTimeMillis(); 
      System.out.println("Time of execution " + (end - start) + " ms"); 
     })); 
     start = System.currentTimeMillis(); 
     final int NUMBER_OF_THREADS = 1000; 
     List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>()); 
     ArrayList<Thread> consumerThreadList = new ArrayList<Thread>(); 
     for (int i = 0; i < NUMBER_OF_THREADS; i++) { 
      Thread t = new Thread(new Consumer(list)); 
      consumerThreadList.add(t); 
      t.start(); 
     } 

     ArrayList<Thread> producerThreadList = new ArrayList<Thread>(); 
     for (int i = 0; i < NUMBER_OF_THREADS/10; i++) { 
      Thread t = new Thread(new Producer(list)); 
      producerThreadList.add(t); 
      t.start(); 
     } 

     // System.out.println("Printing the First Element : " + threadSafeArrayList.get(1)); 

    } 

} 

class Consumer implements Runnable { 
    public final static int NUMBER_OF_OPERATIONS = 100; 
    List<Integer> list; 

    public Consumer(List<Integer> list) { 
     this.list = list; 
    } 

    @Override 
    public void run() { 
     for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { 
      if (list.size() > 0) 
       list.get((int) (Math.random() * list.size())); 
     } 
    } 

} 

class Producer implements Runnable { 
    public final static int NUMBER_OF_OPERATIONS = 100; 
    List<Integer> threadSafeArrayList; 

    public Producer(List<Integer> threadSafeArrayList) { 
     this.threadSafeArrayList = threadSafeArrayList; 
    } 

    @Override 
    public void run() { 
     for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { 
      threadSafeArrayList.add((int) (Math.random() * 1000)); 
     } 
    } 

} 

爲什麼同步採集速度更快,當我有十倍以上讀者不是作家。如何顯示我在許多文章中讀到的RW鎖的進度?

+4

基本閱讀:http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – assylias

+1

值得注意的是'list.get((int)( Math.random()*列表。因爲另一個線程可能會在對size和get的調用之間調用remove,導致IndexOutOfBoundsException如果第一個線程嘗試'從先前的最後一個索引獲得'。雖然這對於你的用例可能不是問題,如果'remove'永遠不會被調用。 – MikeFHay

回答

4

獲取ReadWriteLock的實際成本通常比獲取簡單互斥鎖的成本要慢得多。該javadoc爲ReadWriteLock中進入此:

讀寫鎖是否會提高性能比使用互斥鎖取決於該數據與正在修改讀取頻率,持續時間讀取和寫入操作以及數據爭用 - 即嘗試同時讀取或寫入數據的線程數。例如,一個最初用數據填充並且此後不經常修改,而頻繁搜索(例如某種目錄)的集合是使用讀寫鎖定的理想候選。然而,如果更新變得頻繁,那麼數據的大部分時間都被鎖定,並且幾乎沒有併發的增加。此外,如果讀取操作太短,則讀寫鎖定實現(本質上比互斥鎖定更復雜)的開銷可以支配執行成本,特別是因爲許多讀寫鎖定實現仍然通過小部分代碼。最終,只有分析和測量才能確定使用讀寫鎖是否適合您的應用程序。

因此,您的線程正在執行非常簡單的操作這一事實可能意味着性能主要取決於實際獲取鎖的時間。

您的基準還有另一個問題,那就是Math.random已同步。從其javadoc

此方法已正確同步以允許多個線程正確使用。但是,如果許多線程需要以很快的速率生成僞隨機數,則可能會減少每個線程擁有自己的僞隨機數生成器的爭用。

所以,即使你的併發讀者要的不是在獲取ReadWriteLock中阻止對方,他們可能仍然爭奪在Math.random獲取鎖,擊敗一些使用ReadWriteLock中的上攻。您可以通過改用ThreadLocalRandom來改善這一點。這個類的用法通常應該是這樣的形式:ThreadLocalRandom.current()。nextX(...)(其中X是Int,Long等)。另外,正如assylias指出的那樣,沒有考慮到JIT編譯和其他運行時怪癖的幼稚Java基準測試是不可靠的。你應該使用Java Microbenchmarking Harness (JMH)這樣的基準。