同步方法跟同步方法块 synchronized
- synchronized方法控制对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得改方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占这把锁,知道方法结束才释放锁,后面被阻塞的线程才能获得这个锁被执行
- synchronized关键字可以用在方法上标识锁住this对象
- synchronized(Obj)方法块用在方法内 Obj是被锁的对象
代码示例1 模拟买票
1 package 多线程练习.锁学习; 2 3 public class 不安全线程买票实例 { 4 public static void main(String[] args) { 5 BuyTicket bt = new BuyTicket(); 6 new Thread(bt, "小明").start(); 7 new Thread(bt, "王老师").start(); 8 new Thread(bt, "黄牛党").start(); 9 } 10 } 11 12 13 /* 14 模拟一个买票的线程接口实现类 15 */ 16 class BuyTicket implements Runnable { 17 18 private int ticket = 10; 19 private Boolean flag = true; 20 21 @Override 22 public void run() { 23 while (flag) { 24 buy(); 25 } 26 } 27 28 /* 29 买票方法 30 */ 31 public synchronized void buy() { 32 // 票卖完了 33 if (ticket <= 0) { 34 flag = false; 35 return; 36 } 37 38 // 模拟延时 39 try { 40 Thread.sleep(100); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 45 //模拟买票 46 System.out.println(Thread.currentThread().getName() + " 买到了第 " + ticket-- + " 张表"); 47 } 48 }
输出结果
代码示例2 模拟银行取钱
1 package 多线程练习.锁学习; 2 3 import java.math.BigDecimal; 4 5 /* 6 模拟两个人同时取钱的情况 7 */ 8 public class SafeAccount { 9 public static void main(String[] args) { 10 Bank bank = new Bank("12345", new BigDecimal("100")); 11 WithDrawMoney xiaoming = new WithDrawMoney(bank, new BigDecimal("50"), "小明"); 12 WithDrawMoney xiaomingxf = new WithDrawMoney(bank, new BigDecimal("100"), "小明媳妇"); 13 xiaoming.start(); 14 xiaomingxf.start(); 15 16 } 17 } 18 19 20 /* 21 银行类 22 */ 23 class Bank { 24 String cardNumer; // 卡号 25 BigDecimal balance; // 余额 小tip:Java中用BigDecimal 来表示金钱类型 26 27 public Bank(String cardNumer, BigDecimal balance) { 28 this.cardNumer = cardNumer; 29 this.balance = balance; 30 } 31 32 } 33 34 class WithDrawMoney extends Thread { 35 // 模拟正式环境全部私有 36 Bank bank; 37 BigDecimal nowMoney = new BigDecimal("0"); // 手里的钱 38 BigDecimal drawMoney; // 取走的钱 39 40 // 构造方法 41 public WithDrawMoney(Bank bank, BigDecimal drawMoney, String name) { 42 super(name); // 调用父类的只能用在第一行 43 this.bank = bank; 44 this.drawMoney = drawMoney; 45 } 46 47 @Override 48 public void run() { 49 50 /* 51 synchronized 用在方法上面 则不需要在方法内加synchronized代码块 用在方法指定的是锁this对象 反射类。 52 synchronized 应当需要锁住的对象 53 */ 54 synchronized (bank) { 55 if (bank.balance.subtract(drawMoney).compareTo(BigDecimal.ZERO) == -1) { 56 System.out.println(this.getName() + " 钱不够了,取钱失败"); // 线程类中this.getName() == Thread.currentThread.getName() 57 return; 58 } 59 60 try { 61 Thread.sleep(500); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 66 nowMoney = nowMoney.add(drawMoney); // 手里的钱 67 bank.balance = (bank.balance.subtract(drawMoney)); // 卡上余额 subtract 减法 add 加法 68 System.out.println(this.getName() + " 手里有 " + nowMoney + "钱"); 69 System.out.println("卡上余额:" + bank.balance); 70 } 71 72 } 73 74 75 }
输出结果
代码示例3 容器
1 package 多线程练习.锁学习; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /* 7 synchronized(Obj) 此处的 是锁住list对象 8 Obj又称同步监视器 9 */ 10 public class SafeList { 11 public static void main(String[] args) { 12 List<String> list = new ArrayList<>(); 13 14 for (int i = 0; i < 10000; i++) { 15 new Thread(() -> { 16 synchronized (list) { 17 list.add(Thread.currentThread().getName()); 18 } 19 }).start(); 20 } 21 22 // Sleep就是为了扩大事情的发展面 23 try { 24 Thread.sleep(3000); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 29 System.out.println("数量为:" + list.size()); 30 31 } 32 }
输出结果