JAVA模拟多线程并发Demo及其解决方案

Account类:

package ThreadSafe;

public class Account {

    private String account;

    private double balance;

    public Account() {
    }

    public Account(String account, double balance) {
        this.account = account;
        this.balance = balance;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public void moneyOut(double money) {
        double bal_before = this.getBalance();
        double bal_after = bal_before - money;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.setBalance(bal_after);
    }
}

线程类

package ThreadSafe;

import java.io.IOException;

public class AccountThread implements Runnable {
    private Account act;

    public AccountThread() {
    }

    public AccountThread(Account act) {
        this.act = act;
    }

    public Account getAct() {
        return act;
    }

    public void setAct(Account act) {
        this.act = act;
    }

    @Override
    public void run() {
        this.act.moneyOut(5000);
        System.out.println(Thread.currentThread().getName() + "取出成功,余额:" + this.act.getBalance());
    }

}

调用方法:

public static void test28() {
        Account act = new Account("act-1", 10000);
        Thread t1 = new Thread(new AccountThread(act));
        t1.setName("xxxxx");
        Thread t2 = new Thread(new AccountThread(act));
        t2.setName("yyyy");

        t1.start();
        t2.start();
    }

解决方案:

public void moneyOut(double money) {
        // 以下代码必须是线程同步的,不能并发;
        // 一个线程把这里的代码全部执行完后,另一个线程才能进来

        /**
         * 线程同步机制的语法是
         * synchronized(){
         * 线程同步代码块
         * }
         * synchronized后面小括号中的是相当关键的,
         * 这个数据必须是多线程共享的,才能达到多线程排队
         * 假设有t1,t2,t3,t4,t5,5个线程,
         * 想让t1,t2,t3,3个线程排队,t4,t5不需要排队,
         * 小括号中一定要写一个对t1,t2,t3是共享的对象,这个对象对t4,t5是不共享的
         * 这里的共享对象是账户对象,就是this
         */
        synchronized (this) {
            double bal_before = this.getBalance();
            double bal_after = bal_before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.setBalance(bal_after);
        }
    }

原理说明:

        在JAVA语言中,任何一个对象都有一把锁,其实这个锁就是标记(只是叫做锁);

假设t1和t2线程并发,开始执行以上代码的时候,肯定会有一个先执行一个后执行,假设t1先执行了,遇到了synchronized,这个时候t1就会自动找到后面共享对象的对象锁,之后占有这把锁,然后执行同步代码块中的程序,直到同步代码块中的程序执行完毕,释放这把锁,期间一直占用这把锁;

假设t1已经占有这把锁,t2也遇到了synchronized关键字,也会去占有后面这个共享对象的对象锁,此时t1已经占有这把锁,t2只能在同步代码块外面等待,直到t1把同步代码块执行完毕,t1归还这把锁后,t2才能占有这把锁,之后t2才能执行同步代码块中的程序,这样就达到了线程排队执行;

其中这个共享对象是最关键的,必须是需要排队的线程对象所共享的;

例子中的共享对象也可以是account实例里面的任何一个元素,synchronized只要传入线程的共享对象,就能触发它的占用锁机制;

//这样写就不是线程安全了,因为会new多个对象,不是同一个
Object obj2 = new Object();
synchronized (obj2) {
    ....
}

//这样写是线程安全的,因为abc在常量池里面,只存在一个,是共享对象,但是会导致所有线程同步
synchronized ("abc") {
    ...
}

t1与t2同步,与t3不同步

        synchronized (this) {
            double bal_before = this.getBalance();
            double bal_after = bal_before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.setBalance(bal_after);
        }


public static void test28() {
        Account act = new Account("act-1", 10000);
        Account act2 = new Account("act-2", 10000);

        Thread t1 = new Thread(new AccountThread(act));
        t1.setName("xxxxx");
        Thread t2 = new Thread(new AccountThread(act));
        t2.setName("yyyy");
        //此时t1与t2同步,与t3不同步
        Thread t3 = new Thread(new AccountThread(act2));
        t2.setName("zzzzz");

        t1.start();
        t2.start();
        t3.start();
}

//如果传入的是abc,那么t1,t2,t3是同步的
synchronized ("abc") {}

synchronized范围越大,效率越低;

下面这样写也可以,但是会降低效率;

public void run() {
        synchronized (act) {
            this.act.moneyOut(5000);
            System.out.println(Thread.currentThread().getName() + "取出成功,余额:" + this.act.getBalance());
        }
    }

 实例方法上的synchronized,此时共享对象必须是this,不能是其他对象,缺乏灵活,并且synchronized出现在方法体上,表示整个方法需要同步,可能会无故扩大同步范围,导致程序的执行效率降低,所以这种方式不常用;

如果共享对象是this,并且整个方法需要同步,此时可以用synchronized修饰方法体,可以节简代码;

public synchronized void moneyOut(double money) {
    ...
}

 synchronized修饰在静态变量上,就表示占用类锁,虽然对象不同,但是对象都属于同一个类,类锁被占用了,其他对象都要等待锁的释放:

public class ClassTest {
    public synchronized static void doSome() {
        System.out.println("doSome begin");

        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("doSome end");
    }

    public synchronized static void doOther() {
        System.out.println("doOther begin");
        System.out.println("doOther end");
    }
}


public class ClassThreadTest implements Runnable {
    ClassTest ct;

    public ClassThreadTest(ClassTest ct) {
        this.ct = ct;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        if (Thread.currentThread().getName() == "t1") {
            this.ct.doSome();
        } else {
            this.ct.doOther();
        }
    }
}

public static void test29() {
        ClassTest ct = new ClassTest();
        ClassTest ct2 = new ClassTest();
        Thread t1 = new Thread(new ClassThreadTest(ct));
        t1.setName("t1");
        Thread t2 = new Thread(new ClassThreadTest(ct2));
        t2.setName("t2");

        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t2.start();
    }

以上的锁也称为排他锁;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

y_w_x_k

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值