`

Java并发笔记之------锁

    博客分类:
  • JAVA
 
阅读更多
接触过并发的朋友相信对java的锁都不陌生,Java大体可以分为两种锁,隐式锁(内置锁)和显示锁;
 
OK,什么是内置锁
很简单,就是我们经常写的同步块--synchronized。
内置锁使用比较简单,再方法上加入synchronized关键字或者在需要调用的地方添加synchronized(Obj){}块即可;
 
这里主要说一下显示锁:
 
OK,什么是显示锁
很简单,通过一个显示的对象,来手动开启和关闭一个锁。Java util的concurrent包下面有一个locks包,包下有一个lock接口,该接口就是实现显示锁的底层类,ReentrantLock类就是最常用的显示锁。
 
为什么要显示锁
优点:显示锁可以使你的代码更加灵活,提供锁可等待、可中断、可重入、公平性策略等机制;在性能方面,jdk5显示锁的性能远远大于内置锁,jdk6中也比内置锁高一些,6中对内置锁做了优化;
 
缺点:显示锁使用的危险性比内置锁要高,比如如果你忘记在finally块中释放锁,程序可以正常运行,但是,这是一个定时炸&弹,一旦爆发,不可控的结果很可怖!另,因为内置锁是在方法描述中添加的关键字,所以,当线程在执行的时候,在线程转储中能看到哪些调用栈帧中获得了哪些锁,非常有利于调试,而显示锁是一个对象,并没有办法看到哪些调用栈帧中获得了哪些锁。
 
显示锁怎么用
显示锁有以下几种:ReentrantLock(保守锁,又有叫互斥锁)、RentrankReadWriteLock(读写锁)
 
1、ReentrantLock示例:
 
class Operations {
	// 创建一个显示锁非公平策略显示锁
	final ReentrantLock rwLock = new ReentrantLock();

	@SuppressWarnings("static-access")
	public void exec(long waitTime) {
		// 获得锁
		rwLock.lock();
		try {
			System.out.println(String.format(
					"Operation - doRead() - I am [%s] read now ", Thread
							.currentThread().getName()));
			Thread.currentThread().sleep(waitTime);
			System.out.println(String.format(
					"Operation - doRead() - I am [%s] read OK.%s", Thread
							.currentThread().getName(), waitTime));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放锁
			rwLock.unlock();
		}
	}
}
 
 
 
public static void main(String[] args) {
		final Operations op = new Operations();
		// 调用线程1,获得锁后,停止1分钟
		Thread r1 = new Thread(new Runnable() {
			@Override
			public void run() {
				op.exec(10000L);
			}
		});
		Thread r2 = new Thread(new Runnable() {
			@Override
			public void run() {
				op.exec(20000L);
			}
		});
		r1.start();
		r2.start();
	}
 
 
看Operations类,显示的使用一定是和try{}catch(){}finally{}一起使用,在finally中一定要释放锁,当调用Reentrant.lock方法后,他会判断计数是否为零,为零则认为对象没有被锁定,计数+1,调用unlock方法后,同理计数-1,如果为0,则释放锁,别的线程可获得该锁。注意,该锁可以被重入,同一个线程可以递归的获取锁!此锁最多支持同一个线程发起的 2147483648 个递归锁。试图超过此限制会导致由锁方法抛出的Error。上面代码输出:
Operation - doRead() - I am [Thread-0] read now 
Operation - doRead() - I am [Thread-0] read OK.10000
Operation - doRead() - I am [Thread-1] read now 
Operation - doRead() - I am [Thread-1] read OK.20000
 
OK,上面是最简单的使用,那么,当有一个线程A在持有锁的时候,又有10个线程在等待锁释放状态,这时候A释放了锁,那么,10个线程是先调用哪个?
上面代码是非公平的策略,即,让等待的10个线程抢锁,谁抢到归谁!如何配置公平策略?在显示锁的构造函数里面,有一个boolean参数,默认false(非公平),如果设置为true则为公平策略,即:哪个等待的时间最长,就优先让哪个线程获得锁。
 
总结:Reentrant实现了一种最标准的互斥锁,每次仅允许一个线程持有锁,但是很多情况下,对于一个公共的内存对象,你可能需要允许多个线程同时去读,仅一个线程去写,这时候就需要用到读-写锁
 
2、RentrankReadWriteLock
说明:
一个资源可以被多个读操作访问,或者只能被一个写操作访问,并且读、写操作不能同时存在。
 
示例:
 
class Operation {
	// 定义一个读写锁
	final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
	// 定义一个读锁
	final Lock r = rwLock.readLock();
	// 定义一个写锁
	final Lock w = rwLock.writeLock();

	@SuppressWarnings("static-access")
	public void doRead(long waitTime) {
		// 获得读锁
		r.lock();
		try {
			System.out.println(String.format(
					"Operation - doRead() - I am [%s] read now ", Thread
							.currentThread().getName()));
			// 持有读锁的状态下休眠
			Thread.currentThread().sleep(waitTime);
			System.out.println(String.format(
					"Operation - doRead() - I am [%s] read OK.%s", Thread
							.currentThread().getName(), waitTime));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放锁
			r.unlock();
		}
	}

	public void doWrite() {
		// 获得一个写锁
		w.lock();
		try {
			System.out.println(String.format(
					"Operation - doRead() - I am [%s] wirte now ", Thread
							.currentThread().getName()));
		} finally {
			// 释放一个写锁
			w.unlock();
		}
	}
}
 
 
public class ReadWriteLockMain {
	public static void main(String[] args) {
		final Operation op = new Operation();
		// 读线程1 休眠10s
		Thread r1 = new Thread(new Runnable() {
			@Override
			public void run() {
				op.doRead(10000L);
			}
		});
		// 读线程2 休眠20s
		Thread r2 = new Thread(new Runnable() {
			@Override
			public void run() {
				op.doRead(20000L);
			}
		});
		// 读线程3 休眠30s
		Thread r3 = new Thread(new Runnable() {
			@Override
			public void run() {
				op.doRead(30000L);
			}
		});
		// 写操作线程
		Thread w1 = new Thread(new Runnable() {
			@Override
			public void run() {
				op.doWrite();
			}
		});
		r1.start();
		r2.start();
		r3.start();
		w1.start();
	}
}
 
 
在操作中,线程r1,r2,r3同时获得读锁,r1休眠10s后释放,r2休眠20s后释放,r3休眠30s释放,w1获取读锁;输出中,r1、r2、r3全部同时打印结果,当全部释放后,w1才能获得锁;
Operation - doRead() - I am [Thread-0] read now 
Operation - doRead() - I am [Thread-1] read now 
Operation - doRead() - I am [Thread-2] read now 
Operation - doRead() - I am [Thread-0] read OK.10000
Operation - doRead() - I am [Thread-1] read OK.20000
Operation - doRead() - I am [Thread-2] read OK.30000
Operation - doRead() - I am [Thread-3] wirte now 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics