import java.math.BigDecimal; import java.sql.Connection; import java.sql.SQLException; import java.util.Map.Entry; import java.util.Random; import java.util.TreeMap;
import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.impl.NutDao; import org.nutz.trans.Atom; import org.nutz.trans.Trans;
import cn.gbase.jiangsu.data.transfer.bean.ShopUser;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class TestTransLevel {
NutDao daoOut = new NutDao(); MysqlDataSource dsOut = null;
TreeMap<Long, String> msgMap = new TreeMap<Long, String>();
private void setOutDataSource() { dsOut = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
// 更新连接信息 dsOut.setServerName("localhost"); dsOut.setDatabaseName("shop"); dsOut.setUser("root"); dsOut.setPassword("123456");
// 设置数据源 daoOut.setDataSource(dsOut); }
private void doWork() throws SQLException, InterruptedException { setOutDataSource();
// 初始为1 daoOut.update(ShopUser.class, Chain.make("accountBalance", 0), Cnd.where("id", "=", 6));
msgMap.clear();
new Thread() { public void run() { updateMoney3(1); } }.start();
new Thread() { public void run() { updateMoney3(2); } }.start();
Thread.sleep(5000);
StringBuilder sb = new StringBuilder(); for (Entry<Long, String> entry : msgMap.entrySet()) { sb.append(entry.getKey()); sb.append(":").append(entry.getValue()).append("\n"); } System.out.println(sb.toString());
}
private void updateMoney(final int x) {
// 有事务的话,多个sql执行使用的是同一个connection // 无事务的话,不论select还是update,每次都是新开的一个connection Trans.exec(Connection.TRANSACTION_READ_COMMITTED, new Atom() { public void run() {
// 执行过程:线程2更新,线程1检索&commit,线程2检索&commit // TRANSACTION_READ_UNCOMMITTED时,线程1的检索结果和2的一样,也就是看到了线程2未提交的数据。 // TRANSACTION_READ_COMMITTED时,线程1的检索结果是0,没看到线程2未提交的数据。
// 更新 if (x == 2) { BigDecimal addMoney = BigDecimal.valueOf((new Random().nextInt(300))); daoOut.update(ShopUser.class, Chain.makeSpecial("accountBalance", "+" + addMoney), Cnd.where("id", "=", 6)); echo(Thread.currentThread().getName() + "----add money--" + addMoney, System.nanoTime()); }
// 随机延迟 try { if (x == 1) { Thread.sleep(2000); } else { Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); }
// 读取 ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(Thread.currentThread().getName() + "----money--" + money, System.nanoTime()); } });
}
private void updateMoney2(final int x) {
// 有事务的话,多个sql执行使用的是同一个connection // 无事务的话,不论select还是update,每次都是新开的一个connection Trans.exec(Connection.TRANSACTION_REPEATABLE_READ, new Atom() { public void run() {
// 执行过程:线程2更新,线程1第一次检索,线程2检索&commit,线程1再次检索&commit,出了事务再次检索 // TRANSACTION_REPEATABLE_READ时,线程1的检索结果是0,第二次仍然是0.出了事务,则马上能看到线程2的更新结果 // 如果没有第一次检索,只有第二次检索,则结果与线程2相同。 // 所以,重复读,重点是,之前有过一次检索才行。否则和TRANSACTION_READ_COMMITTED没区别。 // 如果想,在事务内两次读取的一样,就用这个级别。
// 更新 if (x == 2) { BigDecimal addMoney = BigDecimal.valueOf((new Random().nextInt(300))); daoOut.update(ShopUser.class, Chain.makeSpecial("accountBalance", "+" + addMoney), Cnd.where("id", "=", 6)); echo(Thread.currentThread().getName() + "----add money--" + addMoney, System.nanoTime()); } else { // 读取 ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(Thread.currentThread().getName() + "----money--" + money, System.nanoTime()); }
// 随机延迟 try { if (x == 2) { Thread.sleep(1000); } else { Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); }
// 读取 ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(Thread.currentThread().getName() + "----money--" + money, System.nanoTime()); } });
// 读取 ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(Thread.currentThread().getName() + "----money--" + money, System.nanoTime());
}
private void updateMoney3(final int x) {
// 有事务的话,多个sql执行使用的是同一个connection // 无事务的话,不论select还是update,每次都是新开的一个connection Trans.exec(Connection.TRANSACTION_SERIALIZABLE, new Atom() { public void run() {
// 执行过程:线程1查询,线程2更新…… // TRANSACTION_SERIALIZABLE时,线程1的查询结果是0,然后就给加锁了。加的共享锁 // 然后,貌似是一直到事务结束才释放掉,然后线程2才更新上~~ // 如果每个事务都是先读,后update,那么死锁太容易发生了。 // 因为查询加的是共享锁,所以N个事务可以同时执行完查询,等待update, // 都等别的事务结束,好释放锁,结果哪个都结束不了,这就是死锁呗~~ // mysql会选择干掉一些事务。
// 其它几个事务级别,select语句都不会加锁。
// 随机延迟 try { if (x == 2) { Thread.sleep(1000);
// 1秒后更新 BigDecimal addMoney = BigDecimal.valueOf((new Random().nextInt(300))); daoOut.update(ShopUser.class, Chain.makeSpecial("accountBalance", "+" + addMoney), Cnd.where("id", "=", 6)); echo(Thread.currentThread().getName() + "----add money--" + addMoney, System.nanoTime());
} else {
// 读取 ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(Thread.currentThread().getName() + "----money--" + money, System.nanoTime());
// 查询然后等10秒 Thread.sleep(10000); } } catch (InterruptedException e) { e.printStackTrace(); }
// 读取 if (x == 1) { ShopUser u = daoOut.fetch(ShopUser.class, 6); BigDecimal money = u.getAccountBalance(); echo(Thread.currentThread().getName() + "----money--" + money, System.nanoTime()); } } });
}
private synchronized void echo(String msg, long time) { // msgMap.put(time, msg); System.out.println(msg); }
public static void main(String[] args) { TestTransLevel xx = new TestTransLevel(); try { try { xx.doWork(); } catch (InterruptedException e) { } } catch (SQLException e) { e.printStackTrace(); } }
}
|