Java Multithreading 13: Comparison of Read-Write Locks and Two Synchronization Methods

Reprinted from: http://www.cnblogs.com/xrq730/p/4855631.html


Overview of ReentrantReadWriteLock

A very important piece of content in a large website is the reading and writing of data. Although ReentrantLock has the effect of complete mutual exclusion (that is, only one thread is executing the task behind the lock at the same time), its efficiency is very low. Therefore, a read-write lock ReentrantReadWriteLock is provided in the JDK, which can speed up the operation efficiency.

Read-write locks represent two locks, one is a lock related to read operations, called a shared lock ; the other is a lock related to write operations, called an exclusive lock . I understand these two operations as three sentences:

1. Read and read are not mutually exclusive, because read operations will not have thread safety issues

2. Mutual exclusion between write and write to avoid one write operation affecting another write operation, causing thread safety issues

3. Mutual exclusion between read and write, to avoid the write operation modifying the content during the read operation, causing thread safety issues

To sum up, multiple Threads can perform read operations at the same time, but only one Thread is allowed to perform write operations at the same time .

 

read and read sharing

First prove the first sentence "read and read are not mutually exclusive", give a simple example:

copy code
public class ThreadDomain48 extends ReentrantReadWriteLock
{        
    public void read()
    {
        try
        {
            readLock().lock();
            System.out.println(Thread.currentThread().getName() + "Acquired read lock, time is" + 
                    System.currentTimeMillis());
            Thread.sleep(10000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace ();
        }
        finally
        {
            readLock().unlock();
        }
    }
}
copy code
copy code
public static void main(String[] args)
{
    final ThreadDomain48 td = new ThreadDomain48();
    Runnable readRunnable = new Runnable()
    {
        public void run()
        {
            td.read();
        }
    };
    Thread t0 = new Thread(readRunnable);
    Thread t1 = new Thread(readRunnable);
    t0.start();
    t1.start();
}
copy code

看一下运行结果:

Thread-0获得了读锁, 时间为1444019668424
Thread-1获得了读锁, 时间为1444019668424

尽管方法加了锁,还休眠了10秒,但是两个线程还是几乎同时执行lock()方法后面的代码,看时间就知道了。说明lock.readLock()读锁可以提高程序运行效率,允许多个线程同时执行lock()方法后面的代码

 

写和写互斥

再证明一下第二句话"写和写之间互斥",类似的证明方法:

copy code
public class ThreadDomain48 extends ReentrantReadWriteLock
{        
    public void write()
    {
        try
        {
            writeLock().lock();
            System.out.println(Thread.currentThread().getName() + "获得了写锁, 时间为" + 
                    System.currentTimeMillis());
            Thread.sleep(10000);
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            writeLock().unlock();
        }
    }
}
copy code
copy code
public static void main(String[] args)
{
    final ThreadDomain48 td = new ThreadDomain48();
    Runnable readRunnable = new Runnable()
    {
        public void run()
        {
            td.write();
        }
    };
    Thread t0 = new Thread(readRunnable);
    Thread t1 = new Thread(readRunnable);
    t0.start();
    t1.start();
}
copy code

看一下运行结果:

Thread-0获得了写锁, 时间为1444021393325
Thread-1获得了写锁, 时间为1444021403325

从时间上就可以看出来,10000ms即10s,和代码里一致,证明了读和读之间是互斥的

 

读和写互斥

最后证明一下第三句话"读和写之间互斥",证明方法无非是把上面二者结合起来而已,看一下:

copy code
public class ThreadDomain48 extends ReentrantReadWriteLock
{        
    public void write()
    {
        try
        {
            writeLock().lock();
            System.out.println(Thread.currentThread().getName() + "获得了写锁, 时间为" + 
                    System.currentTimeMillis());
            Thread.sleep(10000);
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            writeLock().unlock();
        }
    }
    
    public void read()
    {
        try
        {
            readLock().lock();
            System.out.println(Thread.currentThread().getName() + "获得了读锁, 时间为" + 
                    System.currentTimeMillis());
            Thread.sleep(10000);
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            readLock().unlock();
        }
    }
}
copy code
copy code
public static void main(String[] args)
    {
        final ThreadDomain48 td = new ThreadDomain48();
        Runnable readRunnable = new Runnable()
        {
            public void run()
            {
                td.read();
            }
        };
        Runnable writeRunnable = new Runnable()
        {
            public void run()
            {
                td.write();
            }
        };
        Thread t0 = new Thread(readRunnable);
        Thread t1 = new Thread(writeRunnable);
        t0.start();
        t1.start();
    }
copy code

看一下运行结果:

Thread-0获得了读锁, 时间为1444021679203
Thread-1获得了写锁, 时间为1444021689204

从时间上看,也是10000ms即10s,和代码里面是一致的,证明了读和写之间是互斥的。注意一下,"读和写互斥"和"写和读互斥"是两种不同的场景,但是证明方式和结论是一致的,所以就不证明了。

 

synchronized和ReentrantLock的对比

到现在,看到多线程中,锁定的方式有2种:synchronized和ReentrantLock。两种锁定方式各有优劣,下面简单对比一下:

1. Synchronized is a keyword, just like if...else..., it is an implementation at the syntax level, so synchronized acquiring locks and releasing locks are all done by the Java virtual machine to help users; ReentrantLock is a class-level implementation, so The acquisition of locks and the release of locks require users to operate by themselves. Special reminder again, ReentrantLock is finished after lock(), you must manually unlock()

2. Synchronized is simple, simple means inflexible, and the locking mechanism of ReentrantLock provides great flexibility for users to use. This is reflected most vividly in Hashtable and ConcurrentHashMap. A synchronized lock locks the entire Hash table, while ConcurrentHashMap uses ReentrantLock to achieve lock separation, and the lock is only the segment instead of the entire Hash table

3. Synchronized is an unfair lock, and ReentrantLock can specify whether the lock is fair or unfair

4. The threads notified by synchronized implements the waiting/notification mechanism are random, and ReentrantLock implements the waiting/notification mechanism to selectively notify

5. Compared with synchronized, ReentrantLock provides users with multiple methods for acquiring lock information, such as knowing whether the lock is acquired by the current thread, how many times the lock is called by the same thread, whether the lock is acquired by any thread, etc.

To sum up, I think if you only need to lock simple methods and simple code blocks, then consider using synchronized, and consider using ReentrantLock in complex multi-threading scenarios . Of course, this is only a suggestion, and it still needs to be analyzed in a specific scenario.

Finally, after checking a lot of information, the JDK1.5 version only has many optimizations for synchronized, and the efficiency of synchronized and ReentrantLock should be similar.


Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326825173&siteId=291194637