Java并发库(十一、十二):线程锁、读写锁

深切怀念传智播客张孝祥老师,特将其代表作——Java并发库视频研读两遍,受益颇丰,记以后阅

11.java5的线程锁技术

java.util.concurrent.locks         为锁和等待条件提供一个框架的接口和类,

接口摘要

Condition

Condition 将 Object 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。

Lock

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

ReadWriteLock

ReadWriteLock 维护了一对相关的,一个用于只读操作,另一个用于写入操作。

类摘要

AbstractOwnableSynchronizer

可以由线程以独占方式拥有的同步器。

AbstractQueuedLongSynchronizer

以 long 形式维护同步状态的一个 AbstractQueuedSynchronizer 版本。

AbstractQueuedSynchronizer

为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架。

LockSupport

用来创建锁和其他同步类的基本线程阻塞原语。

ReentrantLock

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantReadWriteLock

支持与 ReentrantLock 类似语义的ReadWriteLock 实现。

ReentrantReadWriteLock.ReadLock

ReentrantReadWriteLock.readLock() 方法返回的锁。

ReentrantReadWriteLock.WriteLock

ReentrantReadWriteLock.writeLock() 方法返回的锁。

     

       Lock比传统线程模型中的synchronized更加面向对象,锁本身也是一个对象,两个线程执行的代码要实现同步互斥效果,就要使用同一个锁对象。锁要上在要操作的资源类的内部方法中,而不是线程代码中。

public interface Lock

所有已知实现类:

ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock

随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句:

    Lock l = ...;

    l.lock();

    try {

        // access the resource protected by this lock

     } finally {

         l.unlock();

    }

锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。

方法摘要

 void

lock()           获取锁。

 void

lockInterruptibly()           如果当前线程未被中断,则获取锁。

 Condition

newCondition()           返回绑定到此 Lock 实例的新Condition 实例。

 boolean

tryLock()           仅在调用时锁为空闲状态才获取该锁。

 boolean

tryLock(long time,TimeUnit unit)           如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。

 void

unlock()           释放锁。

Lock与synchronized对比,打印字符串例子

 


public class LockTest {

 

       /**

        * @param args

        */

       publicstatic void main(String[] args) {

              newLockTest().init();

       }

      

       privatevoid init(){

              finalOutputer outputer = new Outputer();

              newThread(new Runnable(){

                     @Override

                     publicvoid run() {

                            while(true){

                                   try{

                                          Thread.sleep(10);

                                   }catch (InterruptedException e) {

                                          //TODO Auto-generated catch block

                                          e.printStackTrace();

                                   }

                                   outputer.output("zhangxiaoxiang");

                            }

                           

                     }

              }).start();

             

              newThread(new Runnable(){

                     @Override

                     publicvoid run() {

                            while(true){

                                   try{

                                          Thread.sleep(10);

                                   }catch (InterruptedException e) {

                                          //TODO Auto-generated catch block

                                          e.printStackTrace();

                                   }

                                   outputer.output("lihuoming");

                            }

                           

                     }

              }).start();

             

       }

 

       staticclass Outputer{

              Locklock = new ReentrantLock();

              publicvoid output(String name){

                     intlen = name.length();

                     lock.lock();

                     try{

                            for(inti=0;i<len;i++){

                                   System.out.print(name.charAt(i));

                            }

                            System.out.println();

                     }finally{

                            lock.unlock();

                     }

              }

             

              publicsynchronized void output2(String name){

                     intlen = name.length();

                     for(inti=0;i<len;i++){

                                   System.out.print(name.charAt(i));

                     }

                     System.out.println();

              }

             

              publicstatic synchronized void output3(String name){

                     intlen = name.length();

                     for(inti=0;i<len;i++){

                                   System.out.print(name.charAt(i));

                     }

                     System.out.println();

              }    

       }

}

public class LockTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new LockTest().init();
	}
	
	private void init(){
		final Outputer outputer = new Outputer();
		new Thread(new Runnable(){
			@Override
			public void run() {
				while(true){
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					outputer.output("zhangxiaoxiang");
				}
				
			}
		}).start();
		
		new Thread(new Runnable(){
			@Override
			public void run() {
				while(true){
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					outputer.output("lihuoming");
				}
				
			}
		}).start();
		
	}

	static class Outputer{
		Lock lock = new ReentrantLock();
		public void output(String name){
			int len = name.length();
			lock.lock();
			try{
				for(int i=0;i<len;i++){
					System.out.print(name.charAt(i));
				}
				System.out.println();
			}finally{
				lock.unlock();
			}
		}
		
		public synchronized void output2(String name){
			int len = name.length();
			for(int i=0;i<len;i++){
					System.out.print(name.charAt(i));
			}
			System.out.println();
		}
		
		public static synchronized void output3(String name){
			int len = name.length();
			for(int i=0;i<len;i++){
					System.out.print(name.charAt(i));
			}
			System.out.println();
		}	
	}
}


 

12.java5读写锁技术的妙用

       读写锁,分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,由JVM控制。

ReentrantReadWriteLock

构造方法摘要

ReentrantReadWriteLock()           使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock。

ReentrantReadWriteLock(boolean fair)           使用给定的公平策略创建一个新的 ReentrantReadWriteLock。

 

方法摘要

protected  Thread

getOwner()           返回当前拥有写入锁的线程,如果没有这样的线程,则返回 null。

protected  Collection<Thread>

getQueuedReaderThreads()           返回一个 collection,它包含可能正在等待获取读取锁的线程。

protected  Collection<Thread>

getQueuedThreads()           返回一个 collection,它包含可能正在等待获取读取或写入锁的线程。

protected  Collection<Thread>

getQueuedWriterThreads()           返回一个 collection,它包含可能正在等待获取写入锁的线程。

 int

getQueueLength()           返回等待获取读取或写入锁的线程估计数目。

 int

getReadHoldCount()           查询当前线程在此锁上保持的重入读取锁数量。

 int

getReadLockCount()           查询为此锁保持的读取锁数量。

protected  Collection<Thread>

getWaitingThreads(Condition condition)           返回一个 collection,它包含可能正在等待与写入锁相关的给定条件的那些线程。

 int

getWaitQueueLength(Condition condition)           返回正等待与写入锁相关的给定条件的线程估计数目。

 int

getWriteHoldCount()           查询当前线程在此锁上保持的重入写入锁数量。

 boolean

hasQueuedThread(Thread thread)           查询是否给定线程正在等待获取读取或写入锁。

 boolean

hasQueuedThreads()           查询是否所有的线程正在等待获取读取或写入锁。

 boolean

hasWaiters(Condition condition)           查询是否有些线程正在等待与写入锁有关的给定条件。

 boolean

isFair()           如果此锁将公平性设置为 ture,则返回 true。

 boolean

isWriteLocked()           查询是否某个线程保持了写入锁。

 boolean

isWriteLockedByCurrentThread()           查询当前线程是否保持了写入锁。

 ReentrantReadWriteLock.ReadLock

readLock()           返回用于读取操作的锁。

 String

toString()           返回标识此锁及其锁状态的字符串。

 ReentrantReadWriteLock.WriteLock

writeLock()           返回用于写入操作的锁。

         

三个线程读数据,三个线程写数据示例:

可以同时读,读的时候不能写,不能同时写,写的时候不能读

读的时候上读锁,读完解锁;写的时候上写锁,写完解锁。注意finally解锁

package cn.itheima;

 

import java.util.Random;

importjava.util.concurrent.locks.ReadWriteLock;

importjava.util.concurrent.locks.ReentrantReadWriteLock;

 

public class ReadWriteLockDemo

{

       /**读写所使用

        * 三个线程读,三个线程写

        */

       publicstatic void main(String[] args)

       {

              //共享对象

              finalSource source = new Source();

              //创建线程

              for(int i=0; i<3; i++)

              {

                     //读

                     newThread(new Runnable()

                     {

                            publicvoid run()

                            {

                                   while(true)

                                          source.get();

                            }

                     }).start();

                     //写

                     newThread(new Runnable()

                     {

                            publicvoid run()

                            {

                                   while(true)

                                          source.put(newRandom().nextInt(999));

                            }

                     }).start();

              }

       }

 

       staticclass Source

       {

              //共享数据

              privateint data = 0;

              //要操作同一把锁上的读或写锁

              ReadWriteLock rwl =new ReentrantReadWriteLock();

             

              //读方法

              publicvoid get()

              {

                     //上读锁

                     rwl.readLock().lock();

                     try

                     {

                            //获取数据并输出

                            System.out.println("读——"+Thread.currentThread().getName()+"正在获取数据。。。");

                            try

                            {

                                   Thread.sleep(newRandom().nextInt(6)*1000);

                            }catch (InterruptedException e)

                            {

                                   e.printStackTrace();

                            }

                            System.out.println("读——"+Thread.currentThread().getName()+"获取到的数据:"+data);

                     }finally

                     {

                            //解锁

                            rwl.readLock().unlock();

                     }                  

              }

              //写方法

              publicvoid put(int data)

              {

                     //上写锁

                     rwl.writeLock().lock();

                     try

                     {

                            //提示信息

                            System.out.println("写——"+Thread.currentThread().getName()+"正在改写数据。。。");

                            try

                            {

                                   Thread.sleep(newRandom().nextInt(6)*1000);

                            }catch (InterruptedException e)

                            {

                                   e.printStackTrace();

                            }

                            this.data= data;

                            System.out.println("写——"+Thread.currentThread().getName()+"已将数据改写为:"+data);

                     }finally

                     {

                            //解锁

                            rwl.writeLock().unlock();

                     }                  

              }

       }

}


public class ReadWriteLockTest {
	public static void main(String[] args) {
		final Queue3 q3 = new Queue3();
		for(int i=0;i<3;i++)
		{
			new Thread(){
				public void run(){
					while(true){
						q3.get();						
					}
				}
				
			}.start();

			new Thread(){
				public void run(){
					while(true){
						q3.put(new Random().nextInt(10000));
					}
				}			
				
			}.start();
		}
		
	}
}

class Queue3{
	private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
	ReadWriteLock rwl = new ReentrantReadWriteLock();
	public void get(){
		rwl.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName() + " be ready to read data!");
			Thread.sleep((long)(Math.random()*1000));
			System.out.println(Thread.currentThread().getName() + "have read data :" + data);			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			rwl.readLock().unlock();
		}
	}
	
	public void put(Object data){

		rwl.writeLock().lock();
		try {
			System.out.println(Thread.currentThread().getName() + " be ready to write data!");					
			Thread.sleep((long)(Math.random()*1000));
			this.data = data;		
			System.out.println(Thread.currentThread().getName() + " have write data: " + data);					
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			rwl.writeLock().unlock();
		}
		
	
	}
}



JDK帮助文档中的示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):

 class CachedData {//阻塞队列的实现

   Object data;
   volatile boolean cacheValid;    数据有没有标记
   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
   void processCachedData() {处理数据
     rwl.readLock().lock();先上读锁
     if (!cacheValid) {如果数据不存在
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();准备写数据,需先解除读锁
        rwl.writeLock().lock();上写锁
        // Recheck state because another thread might have acquired
        //   write lock and changed state before we did.
        if (!cacheValid) {再次检查数据是否存在,防止其他线程已经存入数据
          data = ...
          cacheValid = true;写好数据,改变标记
        }
        // Downgrade by acquiring read lock before releasing write lock
        准备释放写锁,数据存在了,释放后就要使用数据,恢复产生数据前的读锁状态
         rwl.readLock().lock();
        rwl.writeLock().unlock(); // Unlock write, still hold read
     }
 
     use(data);存在直接使用数据
     rwl.readLock().unlock();解除读锁
   }
 }

 

面试题:设计一个缓存系统

       缓存系统:你要取数据,需调用我的public ObjectgetData(String key)方法,我要检查我内部有没有这个数据,如果有就直接返回,如果没有,就从数据库中查找这个数,查到后将这个数据存入我内部的存储器中,下次再有人来要这个数据,我就直接返回这个数不用再到数据库中找了。              你要取数据不要找数据库,来找我。

class CachedSystem

{     缓存系统的存储器

       privateMap<String, Object> cache = new HashMap<String, Object>();

       取数据方法    可能有多个线程来取数据,没有数据的话又会去数据库查询,需要互斥

       publicsynchronizedObject get(String key)

       {     先查询内部存储器中有没有要的值

              Objectvalue = cache.get(key);

              if(value==null)如果没有,就去数据库中查询,并将查到的结果存入内部存储器中

              {

                     value= “aaaa”; 实际代码是查询后的结果 queryDB(key)

                     cache.put(key,value);

}

return value;

}

}

上面的代码每次只能有一个线程来查询,但只有写的时候才需要互斥,修改如下

来一个读写锁

ReadWriteLockrwl = new ReentrantReadWriteLock();

public Object get(String key)

{    

       上读锁

       rwl.readLock().lock();

先查询内部存储器中有没有要的值

       Objectvalue = cache.get(key);

       if(value==null)如果没有,就去数据库中查询,并将查到的结果存入内部存储器中

       {

              释放读锁 上写锁

              rwl.readLock().unlock();

              rwl.writeLock().lock();

              if(value==null)再次进行判断,防止多个写线程堵在这个地方重复写

              {

                     value = “aaaa”;

                     cache.put(key, value);

              }

设置完成 释放写锁,恢复读写状态

              rwl.readLock().lock();

              rwl.writeLock().unlock();

}

释放读锁

rwl.readLock().unlock();

return value;                                                                 注意:try finallyunlock

}


 

public class CacheDemo {

	private Map<String, Object> cache = new HashMap<String, Object>();

	private ReadWriteLock rwl = new ReentrantReadWriteLock();
	public  Object getData(String key){
		rwl.readLock().lock();
		Object value = null;
		try{
			value = cache.get(key);
			if(value == null){
				rwl.readLock().unlock();
				rwl.writeLock().lock();
				try{
					if(value==null){
						value = "aaaa";//实际失去queryDB();
					}
				}finally{
					rwl.writeLock().unlock();
				}
				rwl.readLock().lock();
			}
		}finally{
			rwl.readLock().unlock();
		}
		return value;
	}
}

你可能感兴趣的:(Java并发库(十一、十二):线程锁、读写锁)