并发
同一个对象被多个线程同时操作
形成条件
队列 + 锁
由于同一个进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。存在以下问题:
//不安全的买票 //线程不安全,有重复或者负数 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 ticketnum = 10; boolean flag = true;//外部停止标志 @Override public void run() { while (flag) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } buy(); } } public void buy() { if(ticketnum <= 0) { flag = false; return; } System.out.println(Thread.currentThread().getName() + "买到了" + ticketnum--); } }
package com.GGp.Demo1; //不安全的取钱 //两个人去银行取钱 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100, "基金"); Drawing you = new Drawing(account, 50, "小明"); Drawing me = new Drawing(account, 100, "小红"); you.start(); me.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; } //取钱 @Override public void run() { if(account.money-drawingMoney<0) { System.out.println(this.getName() +"钱不够取了"); return; } //模拟延迟 放大错误 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 account.money = account.money - drawingMoney; //手内多少钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name + "余额为:" + account.money); System.out.println(this.getName()+"手里有"+nowMoney); } }
/线程不安全的集合 public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()-> { list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size()); } }
//不安全的买票的改进 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 ticketnum = 10; boolean flag = true;//外部停止标志 @Override public void run() { while (flag) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } buy(); } } public synchronized void buy() { if(ticketnum <= 0) { flag = false; return; } System.out.println(Thread.currentThread().getName() + "买到了" + ticketnum--); } }
//不安全的取钱的改进 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100, "基金"); Drawing you = new Drawing(account, 50, "小明"); Drawing me = new Drawing(account, 100, "小红"); you.start(); me.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; } //取钱 @Override public void run() { synchronized (account) { if(account.money-drawingMoney<0) { System.out.println(this.getName() +"钱不够取了"); return; } //模拟延迟 放大错误 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 account.money = account.money - drawingMoney; //手内多少钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name + "余额为:" + account.money); System.out.println(this.getName()+"手里有"+nowMoney); } } }
//线程不安全的集合的改进 public class UnsafeList { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()-> { synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } System.out.println(list.size()); } }
总结
使用同步块时,锁住的时要改变的量,例如案例中的整个账户信息