Lock锁方式解决线程安全问题
在JDK5.0之后新增加了一种更强大的线程同步机制---通过显示定义同步锁来实现线程同步解决线程安全问题。同步锁使用Lock对象充当。
java.util.concurrent.locks.lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的单独访问,每一次只能有一个线程对Lock对象加锁,并且线程在访问共享资源之前应该先加锁。
ReentrantLock类实现了Lock,它拥有和synchronized相同的并发行和内存语义,在实现线程安全的控制中,比较常用的就是ReentrantLock,可以实现显示的加锁和释放锁,比较灵活。
使用Lock锁的方式模拟多线程共同卖票的情景:
1 package com.baozi.java; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 public class WinowTest4 { 7 public static void main(String[] args) { 8 Window4 w1 = new Window4(); 9 Window4 w2 = new Window4(); 10 Window4 w3 = new Window4(); 11 w1.start(); 12 w2.start(); 13 w3.start(); 14 } 15 } 16 17 //通过继承Thread类的方法来实现多线程 18 class Window4 extends Thread { 19 private static int ticket = 100; 20 private static Lock lock = new ReentrantLock(); 21 22 @Override 23 public void run() { 24 while (true) { 25 try { 26 lock.lock(); 27 if (ticket > 0) { 28 try { 29 Thread.sleep(100); 30 } catch (InterruptedException e) { 31 e.printStackTrace(); 32 } 33 System.out.println(Thread.currentThread().getName() + ":" + ticket--); 34 } else { 35 break; 36 } 37 } finally { 38 lock.unlock(); 39 } 40 41 } 42 } 43 }
synchronized和Lock 的异同:
同:两者都是解决线程安全问题
异:
- synchronized关键字定义的不管是同步代码块还是同步方法在执行完成之后都是自动的释放同步监视器(这是一种隐式锁,出了作用域隐式锁将自动的释放)
- lock需要手动的上锁启动同步和手动解锁结束同步;
- Lock只有代码块锁,没办法对某一方法上锁
- Lock锁比较灵活,JVM只需要花费较小的代价来实现线程的调度,性能更好并且有更好的扩展性;
这是一个应用实例:
有两个用户,分三次往同一个账户中存钱,每次每一个人都是存1000元。
分析:
- 是否是多线程问题:是,两个用户是两个线程
- 是否有共享数据:账户余额
- 是否有线程安全问题:有,当两个线程同时往里边存钱时候,如果不进行安全控制,账户余额会出现错误
- 所以考虑使用同步机制解决线程安全问题
1 package com.baozi.exer; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 /** 7 * 银行有一个账户,两个储户分别向里边存三次钱,每次都存一千元, 8 */ 9 public class AccountTest { 10 public static void main(String[] args) { 11 Account account = new Account(0); 12 Customer c1 = new Customer(account); 13 Customer c2 = new Customer(account); 14 c2.setName("储户2"); 15 c1.setName("储户1"); 16 c1.start(); 17 c2.start(); 18 } 19 } 20 21 class Account { 22 private int balance; 23 Lock lock = new ReentrantLock(); 24 25 public Account(int balance) { 26 this.balance = balance; 27 } 28 29 public void setBalance(int amt) { 30 lock.lock(); 31 try { 32 if (amt > 0) { 33 balance += amt; 34 try { 35 Thread.sleep(100); 36 } catch (InterruptedException e) { 37 e.printStackTrace(); 38 } 39 System.out.println(Thread.currentThread().getName() + " :当前余额为: " + balance); 40 } 41 } finally { 42 lock.unlock(); 43 } 44 } 45 46 47 } 48 49 class Customer extends Thread { 50 51 private Account account; 52 53 public Customer(Account account) { 54 this.account = account; 55 } 56 57 @Override 58 public void run() { 59 for (int i = 0; i < 3; i++) { 60 account.setBalance(1000); 61 } 62 } 63 }