锁之编程细节

前言

关于Java锁细节的博客,蛮受读者欢迎的。也有许多不足,比如排版,一些细节等。Java锁细节与锁之编程细节讲述锁的两个不同方面,所以分开写。Java锁细节是讲述每种锁的细节,把锁当做人的话,讲述的是男人,女人,大人,小孩的细节。锁之编程细节讲述的如高性能的使用锁。如同让人怎么跑得块而已。

大纲

编程细节如下:

  1. 锁粗化
  2. 锁细化
  3. 保证public方法线程安全
  4. 分段锁

主体

关于锁粗化与锁细化,在很多关于java优化的书籍都会讲解。比如java并发编程(是老胡并发与锁的启蒙书)等等。一些读者在读Java锁细节说没有讲解锁粗化。在大家的要求,老胡只能厚着面皮写下本博客了。

大家是否觉得十分绕口,锁粗化,锁细化在名词上是两个对立的。为什么都是优化的方法了?其实可以想象成长跑,短跑。就好了。

锁粗化

  1. 把多次的锁合并成一个锁,减少锁消耗,提升执行性能
  2. 在高并发的情况下,减少锁竞争。

receiveIntegerNoLock 方法调用了两个声明synchronized的方法。

    public  IntegeralInfo receiveIntegerNoLock(Integer addIntegeral) {
	    addIntegeral(addIntegeral);
	    addReceiveNum();
	    return this;
	}

	/**
	 * 增加积分
	 * 
	 * @param addIntegeral
	 */
	public synchronized void addIntegeral(Integer addIntegeral) {
	    this.integeralValue += addIntegeral;
	}

	/**
	 * 增加签到次数
	 */
	public synchronized void addReceiveNum() {
	    this.receiveNum++;
	}

新的receiveInteger方法声明了synchronized。值使用了一个锁

public synchronized IntegeralInfo receiveInteger(Integer addIntegeral) {
	    this.integeralValue += addIntegeral;
	    this.receiveNum++;
	    return this;
}

锁细化

  1. 只对需要数据安全的操作上锁。减少锁占用时间。从而提升并发性能
  2. 使用。vm会对进行优化。锁范围为少,优化越好

需求,通过连续签到的天数,计算当次签到应得到多少积分,把计算出的积分增加到用户积分上,并计算除等级。

分析如下

  1. 需要通过连续签到天数,计算当次签到应得到多少积分
  2. 把计算出的积分增加到用户积分上
  3. 并计算除等级
案例
	public synchronized void continuityReceiveIntegeralAlsoGradeSynchronized(Integer continuityDate) {
	    // 通过天数计算,应该获得多少积分
	    int addIntegeral = calculationSignIntegeral(continuityDate);
	    // 添加积分
	    this.integeralValue += addIntegeral;
	    // 计算等级
	    gradeCalculation();
	}
		public int calculationSignIntegeral(Integer continuityDate) {
	    return continuityDate/1;
	}
	/**
	 * 等级计算
	 */
	private void gradeCalculation() {

	}

最简单的实现,没有进行任何的优化。三个操作的代码放到一个方法里面。观察calculationSignIntegeral方法会发现,只是进行数据计算,是一个安全的方法,可以不用上锁。gradeCalculation方法在一定程度上也是安全的方法,未实现。这个需求有点复杂,不知道那个大神,可以分析出来,并实现。

优化之后的是
	public void continuityReceiveIntegeralAlsoGrade(Integer continuityDate) {
			// 通过天数计算,应该获得多少积分
			int addIntegeral = calculationSignIntegeral(continuityDate);
			// 添加积分
			synchronized (this) {
					this.integeralValue += addIntegeral;
			}
			// 计算等级
			gradeCalculation();
	}

只对需要上锁的计算出的积分增加到用户积分上操作进行上锁,减少的上锁范围。缩短持有锁的时间,从而提高并发性能

public都是线程安全

java api里面的类,个个都写得十分优秀。是学习,深入,理解基础技术的优质的代码。解读java.util.Hashtable的源码,从而理解public都是线程安全,而非public方法都是线程不安全的。当你看完java.util.Hashtable源码会发现,所有的public都是线程安全,而非public方法都是线程不安全的。

public都是线程安全,而非public方法都是线程不安全的。这种设计有一下特性

  1. 一种优秀的编码规范,方法层次十分明显,强大的可读性。减少开发成本,维护成本,理解成本,容易定位问题。
  2. 所有public方法的实现只能为锁粗化。
  3. public方法内没有具体的实现,只负责调用private方法。private负责具体的实现。这是一种开闭原则的具体实现。

开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的

分段锁

按照算法计算出的结果或者数据特性进行分类,分类中的每个类型都有一把锁。可以减少锁争夺,更高并行度

关于分段锁最简单的实现还是在java api中,jdk6的ConcurrentHashMap。 ConcurrentHashMap通过hash算法得到key的hash值,在与数组长度求余,余数是数组下标。下标就是分类的类型 下面链接是一个大神写的关于jdk6的ConcurrentHashMap源码解读,写得有点乱,但是解读得十分详细。请点击

完整代码

public class LockUser {

    static class IntegeralInfo {
	// 用户积分
	private Integer integeralValue;

	// 经验值
	private Integer experienceValue;

	// 签到
	private Integer receiveNum;

	// 等级
	private Integer grade;

	/**
	 * 添加积分,同时增加签到次数
	 * 
	 * @param addIntegeral
	 *            需要添加的积分
	 * @return
	 */
	public synchronized IntegeralInfo receiveInteger(Integer addIntegeral) {
	    this.integeralValue += addIntegeral;
	    this.receiveNum++;
	    return this;
	}
	
	public  IntegeralInfo receiveIntegerNoLock(Integer addIntegeral) {
	    addIntegeral(addIntegeral);
	    addReceiveNum();
	    return this;
	}

	/**
	 * 增加积分
	 * 
	 * @param addIntegeral
	 */
	public synchronized void addIntegeral(Integer addIntegeral) {
	    this.integeralValue += addIntegeral;
	}

	/**
	 * 增加签到次数
	 */
	public synchronized void addReceiveNum() {
	    this.receiveNum++;
	}

	public synchronized void continuityReceiveIntegeralAlsoGradeSynchronized(Integer continuityDate) {
	    // 通过天数计算,应该获得多少积分
	    int addIntegeral = calculationSignIntegeral(continuityDate);
	    // 添加积分
	    this.integeralValue += addIntegeral;
	    // 计算等级
	    gradeCalculation();
	}

	public void continuityReceiveIntegeralAlsoGrade(Integer continuityDate) {
	    // 通过天数计算,应该获得多少积分
	    int addIntegeral = calculationSignIntegeral(continuityDate);
	    // 添加积分
	    synchronized (this) {
		this.integeralValue += addIntegeral;
	    }
	    // 计算等级
	    gradeCalculation();
	}

	public int calculationSignIntegeral(Integer continuityDate) {
	    return 1;
	}

	/**
	 * 等级计算
	 */
	private void gradeCalculation() {

	}
    }
}

猜你喜欢

转载自my.oschina.net/u/1261452/blog/1634432
今日推荐