Lock&Condition实现线程同步通信

Lock比传统线程模型中的synchronized方式更加面向对象, 与生活中的锁类似, 锁本身应该是一个对象. 两个线程执行的代码片段要实现同步互斥的效果, 谈么必须用一个Lock对象. 锁是在代表要操作的资源的类的内部方法中, 而不是线程代码中!

ReentrantLock(重入锁)

在需要进行同步的代码部分加上锁,但不要忘记最后一定要释放锁,不然会造成锁永远无法释放,其它线程永远进不来的结果

使用try{ lock.lock() }finally{ lock.unlock() }


我们在使用synchronized的时候,如果需要多线程间进行写作工作, 则需要Object的wait()和notify(),notifyAll()方法进行配合工作

ReentrantReadWriteLock(读写锁)

分为读锁和写锁, 多个读锁不互斥, 读锁与写锁互斥, 写锁锁与写锁互斥, 这是由jvm自己控制的, 你只要上好相应的锁即可. 如果你的代码只读数据, 可以很多人同时读, 但是不能同时写, 那就上读锁; 如果你的代码修改数据, 只能有一个人在写, 且不能同时写, 那就上读锁; 如果你的代码修改数据, 只能有一个人在写, 且不能同时读取, 那就上写锁. 总之, 读的时候上读锁,写的时候上写锁!

核心就是实现了读写分离的锁,在高并发访问下,尤其是读多写少的情况下,性能远远高于重入锁
之前学synchronized,ReentrantLock时,同一时间,只能有一个线程进行访问被锁定的代码, 那么读写锁则不同,其本质是分成两个锁,即读锁,写锁. 在读锁下,多个线程可以并发的进行访问, 但是在写锁的时候,只能一个一个的顺序访问


口诀: 读读共享, 写写互斥, 读写互斥
如果写多读少的话 那么还是使用重入锁 效率高

简单的缓存系统

代码
/**
 * 实现一个缓存系统
 * 
 * @author user
 */
public class CacheDemo {
	// 定义缓存Map
	private Map<String, Object> cache = new HashMap<>();
	// 定义读写锁
	private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

	public Object getData(String key) {
		// 读锁开启 可以共享读
		readWriteLock.readLock().lock();
		Object value = null;
		try {
			value = cache.get(key);
			if (null == value) {// 缓存中不存在该对象
				// 释放读锁 开启写锁
				readWriteLock.readLock().unlock();
				readWriteLock.writeLock().lock();
				// 重复检查 缓存是否有效的状态 因为在我们写数据之前
				// 其他线程可能已经请求了写锁 并且使得缓存有效
				if (null == value) {
					value = "aaa";// 实际去queryDB()
					// 放入缓存中
					cache.put(key, value);
				}
				// 释放写锁 之前先获得写锁
				readWriteLock.readLock().lock();
				readWriteLock.writeLock().unlock();// 写锁释放, 持有读锁

			}
		} finally {
			// 释放读锁
			readWriteLock.readLock().unlock();
		}

		return value;

	}
}

Condition

Condition的功能类似在传统技术中的Object.wait和Object.notify的功能, 在等待Condition时, 允许发生虚假唤醒, 这通常作为对基础平台语义的让步, 杜宇大多数应用程序, 这带来的实际影响很小, 因为Condition应该总是在一个循环中被等待, 并测试正在被等待的状态声明. 某个实现可以随意移除可能的虚假唤醒, 但是建议应用程序程序员总是假定这些虚假唤醒可能发生, 因此总是在一个循环中等待

一个锁内部可以有多个Condition, 即有多路等待和通知, 可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例, 从中除了要体味算法, 还要体味面向对象的封装, 在传统的线程机制中一个监视器对象只能有一路等待和通知, 要想实现多路等待和通知, 必须嵌套使用多个同步监视器对象(如果只用一个Condition, 两个)


那么同样,我们在使用lock的时候,可以使用一个新的等待/通知的类,他就是Condition,这个Condition一定是针对具体的某一把锁的,也就是在只有锁的基础上才会产生Condition
Condition await() Object wait()
多Condition
我们可以通过一个Lock对象产生多个Condition进行多线程的交互,非常的灵活,可以使得部分需要唤醒的线程唤醒,其它线程则继续等待通知

Condition signal() Object notify()

代码

package multi.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionCommunication {

	public static void main(String[] args) {
		final Bussiness bussiness = new Bussiness();

		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 50; i++) {
					bussiness.sub(i);
				}
			}
		}).start();

		for (int i = 1; i <= 50; i++) {
			bussiness.main(i);
		}
	}
}

class Bussiness {
	private boolean flag = true;
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();

	public void sub(int j) {
		lock.lock();
		try {
			// 如果flag为false则等待
			while (!flag) {// 使用while 比较好 可以防止假唤醒
				try {
					condition.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			for (int i = 1; i <= 10; i++) {
				System.out.println("sub : " + i + " 第" + j + "次外循环");
			}
			// 将flag置为false 并唤醒主线程
			flag = false;
			condition.signal();
		} finally {
			lock.unlock();
		}

	}

	public void main(int j) {
		lock.lock();
		try {
			// 如果flag为true则等待
			while (flag) {
				try {
					condition.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			for (int i = 1; i <= 5; i++) {
				System.out.println("main : " + i + " 第" + j + "次外循环");
			}
			// 将flag置为true 并唤醒子线程
			flag = true;
			condition.signal();
		} finally {
			lock.unlock();
		}

	}
}


公平锁/非公平锁 非公平锁效率高 默认非公平






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值