DRP之synchronized及锁的思考

   写这篇博文的背景,是发生在前段时间的评教系统,评教系统出现瘫痪,为了找出问题,大家加班加点,找出的问题就是一个字:“锁”,由于评教系统会频繁的出现学生对数据库的查询,修改,查询,修改,这样就产生了一个进程资源不够,产生死锁!

   在进行DRP项目中,id生成器的原理与我原先评教系统的机制是大致相同的,id加1,然后接着查询id,反复进行此操作,如果一个人,小数据量的在进行,系统不会报错,但是,米老师一直提出“大数据”,我们做出的软件不是一个人在使用。如何避免这样的问题。看下面的代码:

  

package com.bjpowernode.drp.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * id生成器
 * @author Administrator
 *
 */
public class IdGenerator {

	/**
	 * 根据表名生成该表的序列
	 * @param tableName
	 * @return 返回生成的序列
	 */
	<span style="color:#ff0000;">//public static synchronized int generate(String tableName) {
	//public synchronized int generate(String tableName) {
		//synchronized(this) {</span>
	public static int generate(String tableName) {
		//使用数据库的悲观锁for update
		<span style="color:#ff0000;">String sql = "select value from t_table_id where table_name=? for update";</span>
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		int value = 0;
		try {
			conn = DbUtil.getConnection();
			//开始事务
			DbUtil.beginTransaction(conn);
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, tableName);
			rs = pstmt.executeQuery();
			if (!rs.next()) {
				throw new RuntimeException();
			}
			value = rs.getInt("value");
			value++; //自加
			modifyValueField(conn, tableName, value);
			//提交事务
			DbUtil.commitTransaction(conn);
		}catch(Exception e) {
			e.printStackTrace();
			//回滚事务
			DbUtil.rollbackTransaction(conn);
			throw new RuntimeException();
		}finally {
			DbUtil.close(rs);
			DbUtil.close(pstmt);
			DbUtil.resetConnection(conn); //重置Connection的状态
			DbUtil.close(conn);
		}
		return value;
	}
	
	/**
	 * 根据表名更新序列字段的值
	 * @param conn
	 * @param tableName
	 * @param value
	 */
	private static void modifyValueField(Connection conn, String tableName, int value) 
	throws SQLException {
		String sql = "update t_table_id set value=? where table_name=?";
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, value);
			pstmt.setString(2, tableName);
			pstmt.executeUpdate();
		}finally {
			DbUtil.close(pstmt);
		}
	} 
	
	public static void main(String[] args) {
		int retValue = IdGenerator.generate("t_client");
		System.out.println(retValue);
	}
}
    请看上面代码中红色部分,static synchronized与synchronized以及synchronized(this)的区别(如果大家想看详细的介绍请看柳波师哥的博客点击打开链接):

   1.synchronized static是某个类的范围,synchronized static 防止多个线程同时访问这个 类中的synchronized static 方法。它可以对类的所有对象实例起作用。 synchronized 是某实例的范围,synchronized 防止多个线程同时访问这个实例中的synchronized 方法。
   2.synchronized方法与synchronized(this)代码快的区别
        synchronized methods(){} 与synchronized(this){}之间没有什么区别,只是synchronized methods(){} 便于阅读理解,而synchronized(this){}可以更精确的控制冲突限制访问区域,有时候表现更高效率。
   3.synchronized关键字是不能继承的

    这里我想说一下关于死锁出现的情况以及解决的方案,大家看我的代码中红色的查询语句,

    我在cmd窗口中实验了一下:


  回车以后是没有提交数据的!


   在另一个cmd窗口中,看看在红色区域是没有任何数据的!


  出现上面的原因是在第一个窗口中锁住了数据库

  那么第二个cmd窗口如何访问数据呢,在第一个窗口中提交事务或者回滚实务如下图:


  当提交完成后在第二个窗口中就会自动出现下图:


     上面在查询语句中加入了一个for update 这叫做悲观锁。

    锁的概念在软考,操作系统,进程,线程和我们最近的评教系统中,都运用到了,如果将来以后做大数据,我相信锁的运用更是必不可少的!


   

原文地址:https://www.cnblogs.com/xzpblog/p/5117948.html