多线程详解|第九篇、线程同步(解决并发问题)


文章目录

  • 前言
  • 一、线程同步
  • 二、代码示例
    • 1.同步方法解决不安全案例
      • 案例一(买票问题)
      • 案例二(银行取钱问题)
      • 案例三(数组问题)
  • 总结

前言 上一篇我们共同学习了线程优先级和守护线程,本篇我们将一起学习线程同步的问题同时解决我们之前在学习多线程的过程中的并发问题。
一、线程同步
  1. 线程同步机制:
    在处理多线程问题时,多个线程访问同一个对象,并且有可能修改这个对象,这时候就需要线程的同步。线程同步其实就是一种等待机制,多个需要同时访问这个对象的线程,进入这个对象的等待池形成队列,等待前面的一个线程使用完毕,下一个线程在继续使用。
  2. 锁机制:
    由于同一个进程的多个线程访问同一个存储空间,带来方便的同时也带来了冲突,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(synchronized),当一个线程获得对象的排它锁,独占资源,其他的线程必须等待,使用后释放锁机可。
    问题:
    • 一个线程持有锁会导致其他需要此锁的其他线程挂起。
    • 在多线程的竞争下,加锁和释放锁会导致较多的上下文切换和调度延时,引起性能问题。
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
  3. 同步方法:
    • synchronized方法:
      public synchronized void method(int args){};
    • synchronized块:
      synchroized (ojb){};
二、代码示例 1.同步方法解决不安全案例 代码如下(示例):
案例一(买票问题)
1、不安全的情况
//不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"小红").start(); new Thread(buyTicket,"小明").start(); new Thread(buyTicket,"黄牛").start(); } } class BuyTicket implements Runnable{ private int ticketNums = 10; //票 private boolean flag = true; //外部停止方式 //买票 @Override public void run() { while (flag){ try { buy(); }catch (InterruptedException e){ e.printStackTrace(); }} } public void buy() throws InterruptedException{ //判断是否有票 if(ticketNums<=0){ flag = false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票"); } }

运行结果:
多线程详解|第九篇、线程同步(解决并发问题)
文章图片

2、synchronize修饰后
//不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"小红").start(); new Thread(buyTicket,"小明").start(); new Thread(buyTicket,"黄牛").start(); } } class BuyTicket implements Runnable{ private int ticketNums = 10; //票 private boolean flag = true; //外部停止方式 //买票 @Override public void run() { while (flag){ try { buy(); }catch (InterruptedException e){ e.printStackTrace(); }} } public synchronized void buy() throws InterruptedException{ //判断是否有票 if(ticketNums<=0){ flag = false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票"); } }

运行结果:
多线程详解|第九篇、线程同步(解决并发问题)
文章图片

案例二(银行取钱问题)
1、不安全的情况
//不安全的取钱 //两个人去银行取钱 public class UnsfeBank { public static void main(String[] args) { Account account = new Account("结婚基金",100); Drawing you = new Drawing(account,50,"小明"); Drawing girlFriend = new Drawing(account,100,"小红"); you.start(); girlFriend.start(); } } //账户 class Account { int money; String name; public Account(String name,int money) { this.name = name; this.money = money; } } //银行:模拟取钱 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; }@Override public void run() { try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } if(account.money-drawingMoney<0){ System.out.println("对不起,"+Thread.currentThread().getName()+",您的账户余额不足"); return; } //账户余额 account.money = account.money - drawingMoney; //手里的钱 nowMoney = nowMoney + drawingMoney; //Thread.currentThread().getName() = this.getName() System.out.println(account.name + "余额:" + account.money); System.out.println(this.getName() + "手里的钱:" + nowMoney); } }

运行结果:
多线程详解|第九篇、线程同步(解决并发问题)
文章图片

2、synchronize修饰后
//不安全的取钱 //两个人去银行取钱 public class UnsfeBank { public static void main(String[] args) { Account account = new Account("结婚基金",100); Drawing you = new Drawing(account,50,"小明"); Drawing girlFriend = new Drawing(account,100,"小红"); girlFriend.start(); you.start(); } } //账户 class Account { int money; String name; public Account(String name,int money) { this.name = name; this.money = money; } } //银行:模拟取钱 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; }@Override public void run() {synchronized (account){ if(account.money-drawingMoney<0){ System.out.println("对不起,"+Thread.currentThread().getName()+",您的账户余额不足"); return; } try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } //账户余额 account.money = account.money - drawingMoney; //手里的钱 nowMoney = nowMoney + drawingMoney; //Thread.currentThread().getName() = this.getName() System.out.println(account.name + "余额:" + account.money); System.out.println(this.getName() + "手里的钱:" + nowMoney); } } }

运行结果:
多线程详解|第九篇、线程同步(解决并发问题)
文章图片

案例三(数组问题)
1、不安全的情况
//线程不安全的集合 public class Unsafelist { public static void main(String[] args) { List list = new ArrayList(); for(int i = 0; i<10000; i++){ new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(list.size()); } }

运行结果:
多线程详解|第九篇、线程同步(解决并发问题)
文章图片

2、synchronize修饰后
//线程不安全的集合 public class Unsafelist { public static void main(String[] args) { List list = new ArrayList(); for(int i = 0; i<10000; i++){ new Thread(() -> { synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(list.size()); } }

【多线程详解|第九篇、线程同步(解决并发问题)】运行结果:
多线程详解|第九篇、线程同步(解决并发问题)
文章图片

总结

    推荐阅读