java多线程(3)-线程同步

    科技2023-10-19  93

    文章目录

    异步与同步两个不安全案例synchronized

    异步与同步

    多线程由于并发,线程的发生顺序是不可预知的(异步),如:有a,b,c三个线程,那么线程的调度顺序可能时a,b,c;也可能时b,a,c或c,a,b等。处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候我们就需要线程同步,即是让访问此对象的多个线程以某种顺序访问此对象,进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

    两个不安全案例

    下面我们来看看线程异步造成的两个不安全案例: 一.买票问题

    //不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"苦逼的我").start(); new Thread(station,"牛逼的你们").start(); new Thread(station,"可恶的黄牛党").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums = 10; boolean flag = true;//外部停止线程 @Override public void run() { // TODO Auto-generated method stub //买票 while(flag) { buy(); } } private void buy() { //判断是否有票 if(ticketNums<=0) { flag = false; return; } //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }

    执行程序我们会看到有负数票,这是因为线程异步导致前一个线程执行到判断票数时切换到后一个线程,这个时候如果票数是1,那么这张票就会卖给两个人。 二.银行取钱

    //不安全的取钱 //两个人去银行取钱,账户 public class UnfaseBank { public static void main(String[] args) { //账户 Account account = new Account(100,"结婚基金"); Drawing you = new Drawing(account,50,"you"); Drawing girlfriend = new Drawing(account,100,"girlfriend"); you.start(); girlfriend.start(); } } //账户 class Account{ int money;//余额 String name;//卡号 public Account(int money,String name) { this.money = money; this.name = name; } } //银行:模拟取款 class Drawing extends Thread{ Account account;//账户 //取了多少钱 int drawingMoney; //现在手里偶多少钱 int nowMoney; public Drawing(Account account,int drawingMoney,String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } //取钱 public void run() { //判断有没有钱 if(account.money-drawingMoney<0) { System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //卡内余额= 余额-你取的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name+"余额为:"+account.money); System.out.println(this.getName()+"手里的钱:"+nowMoney); } }

    道理和上面一样

    synchronized

    为了同步线程,java提供了一种锁机制,当一个线程获得对象的排他锁,就会独占资源,其他线程必须等待,直到这个线程释放锁。使用锁机制虽然解决了线程异步问题但也会导致性能下降。 synchronized有两种使用方法: 1.将需要互斥访问的方法加上synchronized关键字,如:在买票例子中给buy()方法加上synchronized关键字

    public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"苦逼的我").start(); new Thread(station,"牛逼的你们").start(); new Thread(station,"可恶的黄牛党").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums = 10; boolean flag = true;//外部停止线程 @Override public void run() { // TODO Auto-generated method stub //买票 while(flag) { buy(); } } //synchronized 同步方法,锁的是this private synchronized void buy() { //判断是否有票 if(ticketNums<=0) { flag = false; return; } //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }

    2.synchronized块:用法如下: synchronized(obj){} 其中obj为同步监视器,使用共享资源作同步监视器,{}里面放需要同步的代码块。

    public class UnfaseBank { public static void main(String[] args) { //账户 Account account = new Account(100,"结婚基金"); Drawing you = new Drawing(account,50,"you"); Drawing girlfriend = new Drawing(account,100,"girlfriend"); you.start(); girlfriend.start(); } } //账户 class Account{ int money;//余额 String name;//卡号 public Account(int money,String name) { this.money = money; this.name = name; } } //银行:模拟取款 class Drawing extends Thread{ Account account;//账户 //取了多少钱 int drawingMoney; //现在手里偶多少钱 int nowMoney; public Drawing(Account account,int drawingMoney,String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } //取钱 public void run() { //synchronized块,锁的对象是变化的量,需要增删改的对象 synchronized(account) { //判断有没有钱 if(account.money-drawingMoney<0) { System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //卡内余额= 余额-你取的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name+"余额为:"+account.money); System.out.println(this.getName()+"手里的钱:"+nowMoney); } } }

    Processed: 0.017, SQL: 8