Java多线程之线程同步
2016-07-03 21:13
495 查看
线程同步
线程安全问题
多线程情况下,当多个线程访问同一个数据时,很容易出现线程安全问题。经典的问题——银行取钱问题。几个人同时取一个帐号里的钱,就可能出现问题。下面模拟一下。
//账户类 public class Account { private String accountNo; private double balance; public Account() { } public Account(String accountNo, double balance) { super(); this.accountNo = accountNo; this.balance = balance; } public String getAccountNo() { return accountNo; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } @Override public int hashCode() { return accountNo.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj != null && obj.getClass() == Account.class) { Account target = (Account) obj; return target.getAccountNo().equals(accountNo); } return false; } } //模拟取钱线程 public class DrawThread extends Thread { private Account account; private double withDraw; public DrawThread(String name,Account account, double withDraw) { super(name); this.account = account; this.withDraw = withDraw; } @Override public void run() { //账户余额大于取钱数目 if(account.getBalance() >= withDraw){ System.err.println(getName() + "取钱成功!吐出:" +withDraw+" 文"); //让线程切换 try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } //减去取出的钱 account.setBalance(account.getBalance() - withDraw); System.out.println("\t 余额为: " + account.getBalance()); } else { System.out.println(getName() + "你的余额不足!"); } } } //测试 public class DrawTest { public static void main(String[] args) { Account account = new Account("12345678",10000); new DrawThread("A", account, 8000).start(); new DrawThread("B", account, 8000).start(); } }
结果:
原因:
- run()线程执行体不具有同步安全性
- 程序中有两个并发线程在修改Account对象
同步监视器
为解决上述问题,Java多线程引入同步监视器。其目的:阻止两个线程对同一个共享资源并发访问
通常,推荐使用可能被并发访问的共享资源充当同步监视器。
流程:
加锁->修改->释放锁
同步代码块
使用同步监视器的通用方法就是同步代码块。其含义是:线程开始执行同步代码块前,必须先获得对同步监视器的锁定。因此,修改线程类,使用同步代码块给Account对象上锁。public class DrawThread extends Thread { ... @Override public void run() { synchronized (account) { // 账户余额大于取钱数目 if (account.getBalance() >= withDraw) { System.err.println(getName() + "取钱成功!吐出:" + withDraw + " 文"); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } // 减去取出的钱 account.setBalance(account.getBalance() - withDraw); System.out.println("\t 余额为: " + account.getBalance()); } else { System.out.println(getName() + "你的余额不足!"); } } } ...
同步方法
与同步代码块对应,还有同步方法。使用sychronized关键字修饰方法,无须显示指定同步监视器,同步方法的同步监视器就是this,就是调用该方法的对象。那么,可以修改Account类,提供一个线程安全的draw方法
... public synchronized void draw(double withDraw){ // 账户余额大于取钱数目 if (balance >= withDraw) { System.err.println(Thread.currentThread().getName() + "取钱成功!吐出:" + withDraw + " 文"); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } // 减去取出的钱 balance = balance - withDraw; System.out.println("\t 余额为: " + balance); } else { System.out.println(Thread.currentThread().getName() + "你的余额不足!"); } } ...
同步锁Lock
JDK 1.5开始,Java提供了更强大的线程同步机制- 显式定义同步锁对象来实现同步
- 同步锁由Lock对象充当
与synchronized方法,synchronized代码块相比的优势:
- 更广泛的锁定操作
- 实现更灵活的的结构
- 可以具有差别很大的属性
- 支持多个相关的Condition对象
Lock是控制多个线程对共享资源访问的工具。锁提供了独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源前,须先获得Lock对象
JDK 1.5 提供了Lock,ReadWriteLock两个根接口,ReentrantLock可重入锁是Lock的实现类,ReentrantReadWriteLock是ReadWriteLock的实现类。
JDK 1.8 提供StampedLock类,大多数情况下,可替代传统的ReentrantReadWriteLock.
ReentrantReadWriteLock为读写操作提供3种锁模式:
- Writing
- ReadingOptimistic
- Reading
下面以ReentrantReadWriteLock为例,修改Account类
public class Account { private final ReentrantLock lock = new ReentrantLock(); ... public void draw(double withDraw){ // 加锁 lock.lock(); try { // 账户余额大于取钱数目 if (balance >= withDraw) { System.err.println(Thread.currentThread().getName() + "取钱成功!吐出:" + withDraw + " 文"); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } // 减去取出的钱 balance = balance - withDraw; System.out.println("\t 余额为: " + balance); } else { System.out.println(Thread.currentThread().getName() + "你的余额不足!"); } } finally { // 解锁 lock.unlock(); } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序