安卓复习之旅—Java多线程同步
来源:互联网 发布:剪辑音乐软件 编辑:程序博客网 时间:2024/06/06 09:47
为什么使用多线程
因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。
先看看没有用同步方法的代码:
package threadTest;public class Bank { private int count =0;//账户余额 //存钱 public void addMoney(int money){ count +=money; System.out.println(System.currentTimeMillis()+"存进:"+money); } //取钱 public void getMoney(int money){ if(count-money < 0){ System.out.println("余额不足"); return; } count -=money; System.out.println(+System.currentTimeMillis()+"取出:"+money); } //查询 public void lookMoney(){ System.out.println("账户余额:"+count); } }
package threadTest;public class NormalCode { public static void main(String[] args) { // TODO Auto-generated method stub final Bank bank = new Bank(); Thread tadd = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } bank.addMoney(100); bank.lookMoney(); System.out.println("\n"); } } }); Thread tget = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { bank.getMoney(100); bank.lookMoney(); System.out.println("\n"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); tget.start(); tadd.start(); }}
执行结果:
余额不足账户余额:01481098878098存进:100账户余额:1001481098878098取出:100账户余额:01481098879098存进:100账户余额:01481098879098取出:100账户余额:0余额不足账户余额:1001481098880099存进:100账户余额:100
多线程同步的方法
一使用synchronized关键字修饰方法:
在方法返回值前面加上synchronized即可:
public synchronized void addMoney(int money){ count +=money; System.out.println(System.currentTimeMillis()+"存进:"+money); }
执行结果:
余额不足账户余额:01481099322162存进:100账户余额:1001481099322162取出:100账户余额:01481099323162存进:100账户余额:1001481099323163取出:100账户余额:01481099324162存进:100账户余额:100
二、使用synchronized同步代码块
在主要代码块上用synchronized修饰:
public void addMoney(int money){ synchronized (this) { count +=money; } System.out.println(System.currentTimeMillis()+"存进:"+money); }
执行结果:
余额不足账户余额:01481099322162存进:100账户余额:1001481099322162取出:100账户余额:01481099323162存进:100账户余额:1001481099323163取出:100账户余额:01481099324162存进:100账户余额:100
三、使用特殊域变量(Volatile)实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作(不能被更高等级中断抢夺优先的操作。),它也不能用来修饰final类型的变量
private volatile int count =0;//账户余额
执行结果:
余额不足账户余额:01481099711824存进:100账户余额:1001481099711824取出:100账户余额:01481099712824存进:100账户余额:01481099712824取出:100账户余额:0
执行结果有点意外吧。这是为什么呢?就是因为volatile不能保证原子操作导致的,因此volatile不能代替 synchronized。
四、使用重入锁实现线程同步
ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
ReentrantLock lock = new ReentrantLock(); // 存钱 public void addMoney(int money) { lock.lock(); try { count += money; System.out.println(System.currentTimeMillis() + "存进:" + money); } finally { lock.unlock(); } }
执行结果:
余额不足账户余额:0余额不足账户余额:01481100358054存进:100账户余额:1001481100359054取出:100账户余额:01481100359054存进:100账户余额:100
如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码 。如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁;
五、使用局部变量实现线程同步
private static ThreadLocal<Integer> count = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { // TODO Auto-generated method stub return 0; } }; // 存钱 public void addMoney(int money) { count.set(count.get() + money); System.out.println(System.currentTimeMillis() + "存进:" + money); } // 取钱 public void getMoney(int money) { if (count.get() - money < 0) { System.out.println("余额不足"); return; } count.set(count.get() - money); System.out.println(+System.currentTimeMillis() + "取出:" + money); }
执行结果:
余额不足账户余额:0余额不足账户余额:01481100635097存进:100账户余额:100余额不足账户余额:01481100636098存进:100账户余额:200余额不足账户余额:01481100637098存进:100账户余额:300
看起来只能存不能取?
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变 量副本,而不会对其他线程产生影响。现在明白了吧,原来每个线程运行的都是一个副本,也就是说存钱和取钱是两个账户,知识名字相同而已。所以就会发生上面 的效果。
- 安卓复习之旅—Java多线程同步
- 安卓复习之旅—Java反射机制
- 安卓复习之旅—Java集合类
- Java复习--多线程的同步
- 安卓复习之旅——消息机制
- 安卓复习之旅—hashCode() 和equals()
- 安卓复习之旅—String相关
- 安卓复习之旅—android系统层次结构
- 安卓复习之旅—Activity的启动流程
- 安卓复习之旅—JavaGC机制
- 安卓复习之旅—Matrix类的方法
- 安卓复习之旅—自定义view(一)
- 安卓复习之旅—自定义view(二)
- 安卓复习之旅—观察者模式
- 安卓复习之旅—工厂模式(一)
- 安卓复习之旅—工厂模式(二)
- 安卓复习之旅—工厂模式(三)
- 安卓复习之旅—单例模式
- Windows 上搭建dryrun环境运行github上的Android程序
- CGAffineTransformMake(a,b,c,d,tx,ty) 矩阵
- LevelDB:Bloom源码精读——数据结构
- Vue.js UI框架 - element
- Java断言(assert)介绍及使用
- 安卓复习之旅—Java多线程同步
- C语言指针引用二维数组
- SDL_SoftStretch函数
- iOS UITableView之cellForRowAtIndexPath和indexpathForCell分别获取指定cell和indexpath
- 如何在laravel/lumen中集成支付宝支付
- IOS 自动布局Masonry简介
- MYSQL 提示Please DISCARD the tablespace before IMPORT
- CPU,OS,编译器位数关系
- opencv android 2.4.11使用