秋招准备-Java-并发编程-显式锁Lock(三)

1.Lock接口与其方法,ReentrantLock为实现类

2.Condition接口与其方法,组合ReentrantLock实现条件队列

3.ReentrantReadWriterLock读写锁


1.Lock接口,ReentrantLock实现类

//java.util.concurrent.locks.Lock接口
public interface Lock 
{
	/**
	 * Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作
	 */
    void lock();
    void lockInterruptibly();
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit);
    void unlock();
    Condition newCondition();
}

    1.lock()普通加锁

	Lock lock = new ReentrantLock();
	lock.lock();
	try {
		……
	}finally {
		lock.unlock();
	}

        作用相当与synchronized同步块,用lock()方法进行获取锁,如果进入阻塞状态,也会无限期等待。需要手动解锁,要在try块中进行获取锁后的操作,然后在finally块中unlock()确保解锁。


    2.trylock()尝试获取锁

	if(lock.trylock())
	{
		try {
			……;
		}finally {
			lock.unlock();
		}
	}
	else {
		……
	}

        trylock()提供尝试获取锁的功能,即trylock()后,如果没有线程持锁,则当前线程获取锁,且返回true,即进入try块,进行获取锁的操作,而如果有线程持锁,则不阻塞阻塞等待,且返回false,因此可以直接进行else块做其他事情。

        trylock(long timeout,TimeUnit unit),提供了一段时间的申请,timeout是时间,unit是单位,即在这一段时间内保持请求锁的状态,时间到了还未申请到锁,就返回false放弃了。


    3.lockInterruptibly()可中断获取锁

public class Main
{
	public static void main(String[] args)throws Exception
	{
		Lock lock = new ReentrantLock();
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				lock.lock();
				try {
					Thread.sleep(10000);
				}catch(Exception e) {}
				finally {
					lock.unlock();
				}
			}
		});
		t1.start();
		Thread.sleep(1000);//让t1拿锁
		Thread t2 = new Thread(new Runnable(){
			public void run() {
				try 
				{
					System.out.println("尝试中");
//					lock.lock();
					lock.lockInterruptibly();
					try 
					{
						System.out.println("一直申请,终于得锁");
					}
					finally
					{
						lock.unlock();
					}
				}
				catch(Exception ex) 
				{
					System.out.println("阻塞跳出");
				}
			}
		});
		t2.start();
		Thread.sleep(1000);
		t2.interrupt();
	}
}

        在synchronized和Lock的普通加锁中,如果一旦进入到等待阻塞,则无法跳出,会在没有持锁前一直等待,而Lock提供了可中断的获取锁功能,如果用lockInterruptibly()来获取锁,当前线程可在阻塞期间通过interrupt()方法来抛出异常并跳出。

        在上述代码中,用lockInterruptibly(),t2会在1秒后跳出,用lock()则会10秒后得锁。


    4.公平锁与非公平锁

        涉及这个“公平”概念的有两点,一是多线程阻塞竞争锁时的顺序,二是等待唤醒队列中单个唤醒的顺序。

        而在synchronized中这两点都是随机的。

        在Lock机制下,默认是非公平锁,

        在竞争锁时,可能发生这样的情况,已经有线程在阻塞等待,而此时,当一个新的线程请求锁时,在这同时,该锁变成可用状态,而阻塞线程又还在启动中,这时这个新线程就会跳过这些阻塞线程获得锁。

        显然这是个性能优化,因此非公平锁的性能是要优于公平锁的(基于情况:请求时间短,线程平均持锁时间短),而公平锁则能满足某些情况的功能需求。

        ReentrantLock(boolean fair)的构造方法中,提供了一个参数可以申明为公平锁。在公平锁下,竞争锁与唤醒锁都是按先后顺序的。

        (底层实现还有点迷,不能把非公平情况的顺序讲得很清楚,未……)



2.Condition条件队列

//java.util.concurrent.locks.Condition接口;
public interface Condition 
{
    void await();
    void signal();
    void signalAll();
}

    1.Condition的使用是这样的,通过Lock的newCondition()方法,返回一个与Lock实例绑定的Condition对象,然后在Lock的加锁代码段,用这个condition对象,通过条件判断筛选出一些需要暂缓的线程,然后再通过另一些条件判断来唤醒这些线程。

        因为可以实例化多个Condition对象,就可以通过不同的条件,筛选出满足不同条件的线程暂缓,因此可以用来实现许多功能,如阻塞队列实际就是通过Condition实现的。

        因为唤醒存在虚假唤醒,所以筛选的时候是用while(exp)来筛选的。


    2.写一段实验代码,来实现存取钱功能。

class Bank
{
	public Bank(int num) {account=num;};
	private int account;
	public int Account() {return account;}
	public void save(int num) { account+=num;}
	public void get(int num) { account-=num;}
}
public class Main
{
	public static void main(String[] args)throws Exception
	{
		Bank bank = new Bank(1000);
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		//Get
		for(int i=0;i<10;i++)
		{
			int num = (int)(Math.random()*1000);
			Thread t = new Thread(new Runnable() {
				public void run() {
					lock.lock();
					try {
						while(bank.Account()<num)
						{
							condition.await();
						}
						System.out.print("当前余额:"+bank.Account()+" ");
						System.out.print("取出"+num+" ");
						bank.get(num);
						System.out.println("剩余:"+bank.Account()+" ");		
					}catch(Exception ex) {}
					finally{
						lock.unlock();
					}
				}
			});
			t.start();
		}
		for(int i=0;i<10;i++)
		{
			Thread.sleep(1000);
			int num = (int)(Math.random()*1000);
			Thread t = new Thread(new Runnable() {
				public void run() {
					lock.lock();
					try {
						System.out.print("当前余额:"+bank.Account()+" ");
						System.out.print("存入"+num+" ");
						bank.save(num);
						System.out.println("剩余:"+bank.Account()+" ");	
						condition.signal();
					}catch(Exception ex) {}
					finally{
						lock.unlock();
					}
				}
			});
			t.start();
		}
	}
}

        有时候可能觉得知道了大概的用法,但不知道到底能用在哪,要举例好像也举不出,但实际上自己给需求,自己来做能实验的代码,也是一种培养感觉的方式。



3.ReentrantReadWriteLock

    ReentrantReadWriteLock实现的不是Lock接口,而是ReadWriteLock。因此要这样构造对象:

    ReentrantReadWriteLock rwl = new  ReentrantReadWriteLockd();

     ReentrantReadWriteLock的两个内部类ReadLock和WriteLock实现了Lock接口,因此实际的锁这样获得:

    Lock read = rwl.readLock();

    Lock write = rwl.writeLock();

    即readLock()和writeLock()会返回与读写锁对象对应的两把锁,读锁与写锁。

    在只读操作中加读锁read,则允许多个读操作线程同时共享数据。

    在涉及到写操作的块内加写锁write,一旦有线程进入写锁,则读锁和写锁都开始只允许单线程持锁进入其中一块,即允许读读,不允许读写和写写。

    写段实验代码:

class Bank
{
	public Bank(int num) {account=num;};
	private int account;
	public int Account() {return account;}
	public void save(int num) { account+=num;}
	public void get(int num) { account-=num;}
}
public class Main
{
	public static void main(String[] args)throws Exception
	{
		Bank bank = new Bank(10);
		ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
		Lock read = rwl.readLock();
		Lock write = rwl.writeLock();
		int value = 5;
		Thread th1 = new Thread(new Runnable() {
			public void run() {
				read.lock();
				try {					
					Thread.sleep(1000);
					System.out.println(bank.Account());
				}catch(Exception ex) {}
				finally {
					read.unlock();
				}
			}
		});
		Thread th2 = new Thread(new Runnable() {
			public void run() {
				read.lock();
				try {					
					bank.save(2);
				}catch(Exception ex) {}
				finally {
					read.unlock();
				}
			}
		});
		Thread th3 = new Thread(new Runnable() {
			public void run() {
				write.lock();
				try {
					bank.save(3);
				}catch(Exception ex) {}
				finally {
					write.unlock();
				}
			}
		});
		th1.start();
//		th2.start();
		th3.start();
	}
}

    其实只开读锁的时候就相当于没同步,在里面修改共享数据好像也行,因此也得注意。



4.synchronized与Lock的区别

    1.实现层面

        jvm与jdk

    2.性能层面

        synchronized的局限与优化,Lock的稳定

    3.功能层面

        synchronized简单清晰,但阻塞不可中断,只有一个条件队列。

        Lock的功能性强,tryLock(),lockInterruptibly(),提供多个条件队列,公平锁,读写锁。

   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值