当前位置: 首页 > 科技观察

Java多线程同步常用的三种方法_0

时间:2023-03-12 02:54:51 科技观察

1.为什么要线程同步是因为当我们有多个线程同时访问一个变量或对象时,如果这些线程中同时有读写操作,就会导致变量的值或对象的状态变得混乱,导致程序异常。比如一个银行账户同时被两个线程操作,一个取100元,一个存100元。假设账户原本是0元,如果取款线程和存款线程同时发生,会发生什么情况?提现不成功,账户余额为100,提现成功,账户余额为0,请问是哪个?很难说。所以多线程同步就是为了解决这个问题。二、同步时的代码1、synchronized的锁方法同步方法就是synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当方法被this关键字修饰时,内置锁将保护整个方法。调用该方法前需要获取内置锁,否则会处于阻塞状态。包com.company.model;publicclassBank{privateintcount=0;//账户余额//存钱publicsynchronizedvoidaddMoney(intmoney){count+=money;System.out.println(System.currentTimeMillis()+"存入:"+money);}//取钱publicsynchronizedvoidsubMoney(intmoney){if(count-money<0){System.out.println("余额不足");返回;}数-=钱;System.out.println(+System.currentTimeMillis()+"获取:"+money);}//查询publicvoidlookMoney(){System.out.println("账户余额:"+count);}}测试方法:packagecom.company;importcom.company.model.Bank;publicclassMain{publicstaticvoidmain(String[]args){//在这里写你的代码finalBankbank=newBank();Threadtadd=newThread(newRunnable(){@Overridepublicvoidrun(){//TODO自动生成的方法存根while(true){try{线程.睡眠(1000);}catch(InterruptedExceptione){//TODO自动生成的catch块e.printStackTrace();}bank.addMoney(100);银行.lookMoney();System.out.println("\n");}}});Threadtsub=newThread(newRunnable(){@Overridepublicvoidrun(){//TODO自动生成的方法存根while(true){bank.subMoney(100);bank.lookMoney();System.out.println("\n");try{Thread.sleep(1000);}catch(InterruptedExceptione){//TODO自动生成的catch块e.printStackTrace();}}}});tsub.开始();tadd.开始();}}执行结果:Insufficientbalance账户余额:0Insufficientbalance账户余额:01622020234927入金:100账户余额:1001622020235935入金:100账户余额:2001622020235935出金:100账户余额:1001622020236904同步账户:取出余额关键字也可以修饰静态方法。如果此时调用这个静态方法,整个类就会被锁定。2、同步代码块包com.company.model;publicclassBank{privateintcount=0;//账户余额//存款publicvoidaddMoney(intmoney){synchronized(this){count+=money;}System.out.println(System.currentTimeMillis()+"存入:"+money);}//取钱publicvoidsubMoney(intmoney){if(count-money<0){System.out.println("余额不足");返回;}synchronized(this){count-=money;}System.out.println(+System.currentTimeMillis()+"获取:"+money);}//查询publicvoidlookMoney(){System.out.println("账户余额:"+count);}}效果与方法一类似。注意:同步是一个开销很大的操作,所以同步的内容要尽量减少。通常不需要同步整个方法,只需要使用synchronized代码块同步关键代码即可。3、使用可重入锁实现线程同步在JavaSE5.0中,增加了一个java.util.concurrent包来支持同步。ReentrantLock类是实现Lock接口的可重入互斥锁。它具有与使用同步方法和块相同的基本行为和语义,并扩展了它的功能。ReenreantLock类的常用方法有:ReentrantLock():创建ReentrantLock实例lock():获取锁unlock():释放锁注:ReentrantLock()也有构造方法可以创建公平锁,但是由于它会大大降低运行效率,不推荐。Bank.java代码修改如下:packagecom.company.model;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassBank{privateintcount=0;//AccountBalance//需要声明这个锁privateLocklock=newReentrantLock();//存钱publicvoidaddMoney(intmoney){lock.lock();//锁定尝试{count+=money;System.out.println(System.currentTimeMillis()+"存入:"+money);}catch(Exceptione){lock.unlock();//unlock}finally{lock.unlock();//unlock}}//取钱publicvoidsubMoney(intmoney){lock.lock();//locktry{if(count-money<0){System.out.println("余额不足");返回;}synchronized(this){count-=money;}System.out.println(+System.currentTimeMillis()+"取出:"+money);}catch(Exceptione){lock.unlock();//解决方法Lock}finally{lock.unlock();//unlock}}//querypublicvoidlookMoney(){System.out.println("账户余额:"+count);}}