2.1.线程的同步和协作_基本同步

基本同步

同步概述

多个线程共享一个资源的场景非常常见,比如多个线程读或者写相同的数据等。为了防止共享资源可能出现的错误或数据不一致,需要有一些机制来防止错误的发生。


临界区(Critical Section)是指访问共享资源的代码块,这个代码块在同一时间只允许一个线程执行。Java提供了同步机制来实现临界区。当一个线程试图访问一个临界区时,它使用同步机制查看是不是已经有其他线程进入了临界区,如果美欧它就可以进入临界区;如果已经有线程进入临界区,它就被同步机制挂起,直到进入的线程离开这个临界区。如果在等待进入临界区的线程不止一个,JVM会选择其中的一个,其余将继续等待。


Java中的同步机制有两种:

  • synchronized关键字机制
  • Lock接口及其实现(JDK5.0引入)

本节讨论synchronized机制,这是最基本最常用的同步机制。后面的章节讨论Lock机制。


synchronized

JAVA中最基本的同步方式就是使用synchronized关键字。每一个使用synchronized关键字声明的方法都是临界区,一个对象的临界区在同一时间只有一个允许被访问。synchronized关键字也可以声明在静态方法上,用synchronized声明的静态方法同时只能够被一个执行线程访问,但是其他线程可以访问这个对象的非静态方法。这点要特别注意,因为两个线程可以同时访问一个对象的两个方法,一个是静态的,另一个是非静态的。如果两个方法都改变了相同的数据,将会出现数据不一致的错误。


synchronized是可重入的。


下面以银行转账的经典示例来说明synchronized关键字的用法,这个示例使用两个线程访问同一个账户对象,一个线程转钱到此账户,一个线程从此账户中取钱,如果没有同步机制那么最后账户的余额是不对的,同步机制保证账户的余额最终是正确的,下面的示例将在转入和转出时使用synchronized关键子进行同步。其实更本质的是在修改账户余额时需要同步。

public class AccoutSynchronized {

    public static void  main(String[] args){
        final Account account = new Account(1000);
        System.out.println("main:初始账户。账户余额:" + account.getBalance());

        System.out.println("main:创建工作线程。");
        Thread addThread = new Thread(new AddWorker(account));
        Thread subThread = new Thread(new SubWorker(account));

        System.out.println("main:启动工作线程。");
        addThread.start();
        subThread.start();

        try {
            System.out.println("main:等待工作线程执行完成。");
            addThread.join();
            subThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main:工作线程执行完成。账户余额:" + account.getBalance());

        System.out.println("main:退出。");
    }
}

/** 转入线程**/
class AddWorker implements Runnable{

    private Account account;

    public AddWorker(Account account){
        this.account = account;
    }

    @Override
    public void run(){
        System.out.println("转入工作线程启动。");
        for(int i=0; i<100; i++){
            account.addAmount(1000);
        }

        System.out.println("转入工作线程完成。");
    }
}

/** 转出线程**/
class SubWorker implements Runnable{

    private Account account;

    public SubWorker(Account account){
        this.account = account;
    }

    @Override
    public void run(){
        System.out.println("转出工作线程启动。");
        for(int i=0; i<100; i++){
            account.subAmount(1000);
        }
        System.out.println("转出工作线程完成。");
    }
}

/** 账户 **/
class Account{
    private double balance;

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

    /** 转入账户 */
    public synchronized void addAmount(double amount){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        balance += amount;
    }

    /** 转出账户 */
    public synchronized void subAmount(double amount){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        balance -= amount;
    }

    public double getBalance(){
        return this.balance;
    }
}

程序执行日志如下:

main:初始账户。账户余额:1000.0
main:创建工作线程。
main:启动工作线程。
main:等待工作线程执行完成。
转入工作线程启动。
转出工作线程启动。
转出工作线程完成。
转入工作线程完成。
main:工作线程执行完成。账户余额:1000.0
main:退出。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值