【Java】资源共享有冲突

      开发过程中经常会遇到并发处理某共享数据时,产生不一致的情况,如何解决呢?方案是-----加锁。

一、对线程加锁

      对线程加锁,就是利用Java提供的synchronized关键字。

【修饰一个代码块】

import java.lang.*;
/**
 * 线程同步Demo
 * @author 郑艳霞
 *
 */
public class Test implements Runnable {
	public void run() {
		synchronized (this) {
			for (int i = 0; i < 4; i++) {
				try {
					System.out.println("当前执行的是:"+Thread.currentThread().getName());
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}


	// 测试类
	public static void main(String[] args) {
		Test test = new Test();
		Thread thread1 = new Thread(test, "线程一");
		Thread thread2 = new Thread(test, "线程二");
		thread1.start();
		thread2.start();
	}
}
    运行结果为:


      访问synchronized同步代码块时,其他线程将被阻塞,因为执行synchronized代码块时,会锁定当前对象,只有释放对该对象的锁,下一个线程才能执行。
      补充:synchronized只锁定对象,每个对象都只对应一个锁。如果在测试类中重新再声明一个对象,会证明相同的结论。(不要被单纯的运行结果欺骗哦)

【修饰一个方法】
      其实在上面例子中就等同于是修饰一个方法。简单说,修饰代码块,是指将synchronized(this){//代码块},但是当这段代码块就是整个方法的时候,那就是在修饰一个方法了呗。这个时候就有了另一种写法,即:直接将synchronized关键字放在方法名前面。如下:
public synchronized void run() {
	//synchronized (this) {
		for (int i = 0; i < 4; i++) {
			try {
				System.out.println("当前执行的是:"+Thread.currentThread().getName());
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
//}
      运行结果当然也是一样的,就不在赘述。
【修饰一个类】
       跟上面一样,synchronized(this)锁住的其实就是this的范围,当this只是一个类中的一个方法中的一小段代码时,那锁的范围就是这段代码,这段代码中的对象在同一时刻只能被一个线程调用。如果this可以等同于一个方法,那锁的范围就是一个方法,这段代码中的对象在同一时刻只能被一个线程调用。如果范围是一个类,如下写法:当synchronized(test.class),那锁住的就是一个类,类中所有的对象同用一把锁。

二、对资源加锁

      对资源进行加锁,就是从数据库方面进行控制。悲观锁是基于数据库锁机制使用的。

【悲观锁(Pessimistic Lock)

      悲观锁认为每次拿数据的时候都会有人来修改,认为肯定会出现影响数据完整性一致性的问题产生,所以每次拿数据的时候都会加锁。即,在整个事务处理过程中,数据都处于锁定状态。

这条sql语句锁定了t_table_id表中所有符合检索条件的记录,在这次查询事务提交之前,这些记录都无法被其他人修改。直至本次事务提交,锁释放。
【乐观锁(Optimistic Lock)

      乐观锁认为每次去拿数据都不会有人来修改,所以不加锁。但是在更新时会判断一下再此期间是否有人更新这些数据,所以用到了数据库的版本记录机制,通过version字段来判断是否是过期数据。(但是乐观锁不能解决读脏数据问题)

【比较】
      相对于悲观锁,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,来保证操作最大程度的独占性。但是如果某次事务时间特别长,还使用悲观锁的话,将会导致严重后果。乐观锁在一定程度上解决了这个问题。

【适用】
    乐观锁适用于写比较少的情况,即很少发生冲突的情况,这样就可以省去锁开销,加大了系统的整个吞吐量。
    悲观锁适用于更改频繁的数据表,一开始查询就加锁,直至更新操作结束才释放,但是性能有所下降。
【悲观锁的实现】

package com.bjpowernode.drp.util;


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


/**
 *  Id生成器
 * @author happy
 *
 */
public class IdGenerator {
	public static int generate(String tableName) {
		int value = 0;
		Connection conn = DbUtil.getConnection();
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		//悲观锁
		String sql = "select value from t_table_id where table_name=? for update";
		// 开始事务DbUtil.beginTransaction(conn);
		try {
			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.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
	 * @throws SQLException
	 */
	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);
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值