[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]理解事务级别,试验的代码

上一篇:[备忘]mysql事务级别的详解~
下一篇:[备忘]多个事务,先更新,再select的测试代码

添加日期:2014/11/3 23:25:06 快速返回   返回列表 阅读2546次


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();
        }
    }

}

 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved