Built-in multi-threaded Java lock the display lock

Java has a built-in lock by Synchronized achieved, and display lock ReentrantLock implemented, the benefits of each of these two locks have, be regarded as mutually supplementary, to do a summary of today.

Synchronized

Built-in lock to obtain and release locks are implicit, enter the synchronized code is modified to obtain a lock out the corresponding code lock is released.

synchronized(list){ //获得锁
    list.append();
    list.count();
}//释放锁

communication

Synchronized with the use of supporting communication method usually wait (), notify ().

wait () method immediately release the current lock and enters a wait state, waiting to notify the appropriate and regain lock to continue after the execution; notify () does not immediately immediately release the lock must wait notify () where the thread has finished the implementation of synchronized All the code block will be released. To verify with the following code:

public static void main(String[] args){
    List list = new LinkedList();
    Thread r = new Thread(new ReadList(list));
    Thread w = new Thread(new WriteList(list));
    r.start();
    w.start();
}
class ReadList implements Runnable{

    private List list;

    public ReadList(List list){ this.list = list; }

    @Override
    public void run(){
        System.out.println("ReadList begin at "+System.currentTimeMillis());
        synchronized (list){
            try {
                Thread.sleep(1000);
                System.out.println("list.wait() begin at "+System.currentTimeMillis());
                list.wait();
                System.out.println("list.wait() end at "+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("ReadList end at "+System.currentTimeMillis());

    }
}

class WriteList implements Runnable{

    private List list;

    public WriteList(List list){ this.list = list; }

    @Override
    public void run(){
        System.out.println("WriteList begin at "+System.currentTimeMillis());
        synchronized (list){
            System.out.println("get lock at "+System.currentTimeMillis());
            list.notify();
            System.out.println("list.notify() at "+System.currentTimeMillis());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("get out of block at "+System.currentTimeMillis());
        }
        System.out.println("WriteList end at "+System.currentTimeMillis());

    }
}

operation result

ReadList begin at 1493650526582
WriteList begin at 1493650526582
list.wait() begin at 1493650527584
get lock at 1493650527584
list.notify() at 1493650527584
get out of block at 1493650529584
WriteList end at 1493650529584
list.wait() end at 1493650529584
ReadList end at 1493650529584

Visible reader thread starts running after the start wait, just write thread to acquire the lock; the writer thread out of sync blocks instead notify after reading the thread wait until the end, that is to acquire the lock. So notify will not release the lock, wait will release the lock. It is worth mentioning that, notifyall () will notify all of the threads waiting in the queue.

coding

Coding mode is relatively simple, single, do not have to get a lock displayed, release the lock, can reduce errors due to carelessness forget to release the lock. Use modes are as follows:

synchronized(object){

}

flexibility

  1. Built-in lock when entering the synchronized block, adopted a strategy to wait indefinitely, once you start waiting for it neither interrupted nor canceled, prone to the problem of hunger and deadlock
  2. When a thread calls the notify method will randomly select a thread waiting queue of the corresponding object to wake up, and not in accordance with the FIFO manner, if there is a strong fairness requirements, such as FIFO can not meet

performance

Synchronized JDK1.5 and before the performance (mainly the throughput) is relatively poor, not as scalable ReentrantLock. But after JDK1.6, modified the management of the built-in lock algorithm, making Synchronized and performance standards ReentrantLock little difference.

ReentrantLock

ReentrantLock is a lock, unlock and lock needs to display operation.

communication

ReentrantLock with the prevailing way Condition, as follows:

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
condition.await();//this.wait();
condition.signal();//this.notify();
condition.signalAll();//this.notifyAll();

Condition is bound to Lock must be used lock.newCondition () to create a Condition. As can be seen from the above code, communication can be realized Synchronized, Condition can be achieved, similar function code written in the same row. The Condition of excellence is that it can create different Condition is among multiple threads, such as object read / write Condition, empty / full Condition queue in the source code of ArrayBlockingQueue JDK on the use of this feature:

 public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    notEmpty.signal();
}
private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];
    items[takeIndex] = null;
    if (++takeIndex == items.length)
        takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();
    return x;
}

coding

Lock lock = new ReentrantLock();
lock.lock();
try{

}finally{
    lock.unlock();
}

Synchronized compared to the more complicated, but we must remember to release the lock and not elsewhere in finally in, so as to ensure that even the abnormal can release the lock.

flexibility

  • lock.lockInterruptibly () can make threads wait for a lock to support the response to an interrupt; lock.tryLock () can make a thread waiting for some time after you stop if you have not been waiting for, rather than waiting to obtain a lock. With these two mechanisms can better formulate a retry mechanism to acquire a lock, rather than the blind have been waiting for, you can better avoid starvation and deadlock
  • ReentrantLock can be a fair lock (non-default), the so-called fair locks that lock wait FIFO queue, but fair locks will bring performance overhead, if not necessary is not recommended. This reason reordering CPU instructions are similar, if the force in the writing order to execute the instruction code, it will waste a lot of clock cycles, reach maximum utilization

performance

Although Synchronized and standards ReentrantLock performance is not very different, but ReentrantLock also provides a non-exclusive read-write locks,
which is not mandatory with a maximum of only one thread can hold the lock, it would avoid the "read / write" Conflict "write / write" conflict, but would not rule out "a read / read" conflict,
because the "read / reading" does not affect the integrity of the data, so it can read multiple threads simultaneously holding the lock, so that relatively high literacy under the circumstances, the performance will be greatly improved.

Thread-safe linkedlist below using two locks were implemented:

class RWLockList {//读写锁

    private List list;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public RWLockList(List list){this.list = list;}

    public int get(int k) {
        readLock.lock();
        try {
            return (int)list.get(k);
        } finally {
            readLock.unlock();
        }
    }

    public void put(int value) {
        writeLock.lock();
        try {
            list.add(value);
        } finally {
            writeLock.unlock();
        }
    }
}

class SyncList  {

    private List list;

    public SyncList(List list){this.list = list;}

    public synchronized int  get(int k){
        return (int)list.get(k);
    }

    public synchronized void put(int value){
        list.add(value);
    }

}

Write lock test code:

List list = new LinkedList();
for (int i=0;i<10000;i++){
    list.add(i);
}
RWLockList rwLockList = new RWLockList(list);//初始化数据

Thread writer = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            rwLockList.put(i);
        }
    }
});
Thread reader1 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            rwLockList.get(i);
        }
    }
});
Thread reader2 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            rwLockList.get(i);
        }
    }
});
long begin = System.currentTimeMillis();
writer.start();reader1.start();reader2.start();
try {
    writer.join();
    reader1.join();
    reader2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("RWLockList take "+(System.currentTimeMillis()-begin) + "ms");

Genlock test code:

List list = new LinkedList();
for (int i=0;i<10000;i++){
    list.add(i);
}
SyncList syncList = new SyncList(list);//初始化数据
Thread writerS = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            syncList.put(i);
        }
    }
});
Thread reader1S = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            syncList.get(i);
        }
    }
});
Thread reader2S = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            syncList.get(i);
        }
    }
});
long begin1 = System.currentTimeMillis();
writerS.start();reader1S.start();reader2S.start();
try {
    writerS.join();
    reader1S.join();
    reader2S.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("SyncList take "+(System.currentTimeMillis()-begin1) + "ms");

result:

RWLockList take 248ms
RWLockList take 255ms
RWLockList take 249ms
RWLockList take 224ms

SyncList take 351ms
SyncList take 367ms
SyncList take 315ms
SyncList take 323ms

Visible read-write lock mutex is indeed better than the pure broken

to sum up

Built-in locks biggest advantage is simple to use, display lock biggest advantage is feature-rich, so you can use the built-in lock with built-in lock, built-in lock function can not meet in consideration of the display lock.

Guess you like

Origin blog.51cto.com/14570694/2445458