在做数据库访问的时候,遇到了这样的问题:两个线程同时访问同一对象中的方法,那么就可能会引发数据不一致的问题,那么我们需要做的,就是加上锁。
第一种方案:Synchronized
Java中用来给对象和方法或者代码加锁的,当他锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行该代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只有一个线程得到,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
用法:Synchronized 加入方法上和Synchronized(this)两种写法含义是一样的,都是对对象加锁。
实现方法:
<span style="font-size:18px;"><p>public staticsynchronized int generatate(StringtableName){
//使用数据库的悲观锁for update
String sql ="select value from t_table_id where table_name=?";
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);
}catch(SQLException e) {
e.printStackTrace();
}finally {
DbUtil.close(rs);
DbUtil.close(pstmt);
DbUtil.close(conn);
}
return value;
}
</p></span>
或者我们也可以这样写:
<span style="font-size:18px;">public synchronized int generatate(String tableName){
synchronized(this){</span>
第二种方案:悲观锁
可以使用数据库中的悲观锁,例如:select * from t_table_id where table_name? form update ,悲观锁是采用数据库机制实现的,数据被锁住之后,其他用户将无法查看,直到锁释放,只有提交或回滚事物锁才会释放。for update 语句只能放到 select 语句中,因为查询时把数据锁住才有意义。
我们封装数据回滚:
<span style="font-size:18px;">public static void beginTransaction(Connection conn){
try{
if(conn!= null){
if(conn.getAutoCommit()){
conn.setAutoCommit(false); //手动提交
}
}
}catch(SQLException e){
}
}
</span>
封装事物的提交:
<span style="font-size:18px;">public static void commitTransaction(Connection conn){
try{
if(conn!= null){
if(!conn.getAutoCommit()){
conn.commit();
}
}
}catch(SQLException e){}
}
</span>
封装事物回滚:
<span style="font-size:18px;">public static void rollbackTracsaction(Connection conn){
try{
if(conn!= null){
if(!conn.getAutoCommit()){
conn.rollback();
}
}
}catch(SQLException e){}
}
</span>
封装事物重置:
<span style="font-size:18px;">public static void ressetConnection(Connection conn){
try{
if(conn!= null){
if(conn.getAutoCommit()){
conn.setAutoCommit(false);
}else
{
conn.setAutoCommit(true);
}
}
}catch(SQLException e){}
}
</span>
测试使用:
<span style="font-size:18px;">public static int generatate(String tableName){
//使用数据库的悲观锁for update
String sql ="select value from t_table_id where table_name=? for update";
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(SQLException e) {
e.printStackTrace();
//回滚事物
DbUtil.rollbackTracsaction(conn);
}finally {
DbUtil.close(rs);
DbUtil.close(pstmt);
DbUtil.ressetConnection(conn);//充值到Connection的状态
DbUtil.close(conn);
}
return value;
}
</span>
总结:
数据库中的数据无价,悲观锁,有强烈的独占和排他的特性,对数据被外界修改保持保守态度.在这个数据处理过程,将数据处于锁定状态,这就是悲观锁的实现.