Java 并发编程之对象的共享
来源:互联网 发布:虚拟串口软件 编辑:程序博客网 时间:2024/05/29 08:28
可见性
看下面这段代码
public class Init {public static boolean ready = false;public static int number = 0;public static class Readerthread extends Thread {@Overridepublic void run() {// TODO Auto-generated method stubwhile (!ready)Thread.yield();System.out.println(number);}}public static void main(String[] args) {new Readerthread().start();number = 42;ready = true;}}
按照推论来讲,程序应该会输出42这个数字,Thread.yield();是将线程打回就绪状态,等待系统的下一次调度。实际上可能不是,程序可能输出0,或者 永远循环下去。读线程可能看到了ready的值,但却没有看到写入后number的值 。这种现象被叫做“重排序”,也就是说实际的执行顺序可能是先写入ready后写入Number。我也运行了几十次这个程序,倒是都会输出42.可能出错的机率比较小吧。解决这个问题的方法就是添加足够的同步。
几天之后我在项目中碰到了比这更好的例子
public class Init {public static boolean ready = false;public static int number = 0;public static class Readerthread extends Thread {@Overridepublic void run() {// TODO Auto-generated method stubnumber = 42;ready = true;}}public static void main(String[] args) {while (!ready)Thread.yield();System.out.println(number);new Readerthread().start();}}这个循环会100%的死循环下去。如何使此对象共享于两个线程之间呢
我想到了volatile关键字,用于确保可见性
public class Init {public volatile boolean ready = false;public volatile int number = 0;public class Readerthread extends Thread {@Overridepublic void run() {// TODO Auto-generated method stubnumber = 42;ready = true;}}public void test() {while (!this.ready) {Thread.yield();System.out.println(this.number);}System.out.println(this.number);new Readerthread().start();}public static void main(String[] args) {Init init = new Init();init.test();}}
结果却很出乎意料。程序依旧死循环,再仔细一看就明白了。。原来这和并发编程没什么 关系。只是单纯的卡住了
如何解决。。很简单
public class Init {public volatile boolean ready = false;public volatile int number = 0;public class Readerthread extends Thread {@Overridepublic void run() {// TODO Auto-generated method stubnumber = 42;ready = true;System.out.println(ready);}}public void test() {new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubwhile (!ready) {Thread.yield();System.out.println(number);}}}).start();System.out.println(this.number);new Readerthread().start();}public static void main(String[] args) {Init init = new Init();init.test();}}
失效数据
public class Init {private int vlaue;/** * @return the vlaue */public int getVlaue() {return vlaue;}/** * @param vlaue * the vlaue to set */public void setVlaue(int vlaue) {this.vlaue = vlaue;}}
上面是一个非线程安全的整数类,第一眼看完可能都会认为应该对set方法进行同步 ,其实 不然。因为可能两个线程一个在调用set,另一个再用get.那么用get的那个线程可能看到更新后的value的值 ,也可能看不到。所以如果仅对set方法进行同步是不对的,也应该对get方法进行同步
public class Init {private int vlaue;/** * @return the vlaue */public synchronized int getVlaue() {return vlaue;}/** * @param vlaue * the vlaue to set */public synchronized void setVlaue(int vlaue) {this.vlaue = vlaue;}}
非原子的64位操作
读到一个失效值 也比读到一个随机值好吧。这也是一种安全性保证 ,不过被称为最低安全性。
存在一个例外。java的内存模型要求,变量的读取和写入操作都必须是原子操作,但对于非volatile类型的Long和double变量 ,jvm允许将64位的读写 操作分解为两个32位的操作。只读一半的话,,,结果可想而知。解决办法则是用volatile关键字声明它们,或者加锁保护起来。
加锁与可见性
加锁的含义不仅仅局限于互斥行为,还包括内存可见性,为确保所有线程都能看到共享变量的最新值,那么所有执行读或写操作的线程都必须在同一个锁上进行同步 。换句话说就是在加锁和解锁之间的代码块里的内容都是线程可见的。
Volatile变量
这是java提供的一种稍弱的同步机制,用于确保变量的更新通知到其它线程。它只确保可见性而不可确保原子性。用于循环中的中止变量等地方比较合适,等其它一些不需要准确的数值的地方。以下三种情况才应该使用
- 对变量的写入操作不依赖变量的当前值,(如Count++就不行)或者能确保是在单线程中更新变量值
- 该变量不会与其他状态变量 一起纳入不变性条件中。(这个的意思 是不会影响到其它变量为前提吧?)
- 在访问变量时不需要加锁
发布与逸出
发布一个对象 的意思 是指,使对象能够在当前作用域之外的代码中使用,例如,将一个指向该对象的引用保存到其他代码可以访问的地方。许多情况下我们要确保对象的内部状态不被 发布。如果发布了那么就会破坏线程安全性。当不该发布的对象被发布时这种情况被 叫做逸出。如下面这种情况。
public class unsafe {private String[] state = new String[] { "sdf", "fdsf" };public String[] getstate() {return state;}}常见的还有隐式的this逸出
如
button.setonclicklistener(new onclicklistener(){public void onEvent(Event e){dosomething(e);}});
- Java 并发编程之对象的共享
- Java 并发编程之对象的共享(二)
- java并发编程基础之对象的共享
- java 并发编程实战 之 对象的共享
- java并发编程实践之对象的共享
- java并发编程实战-对象的共享
- [Java并发编程实战] 对象的共享
- java并发编程实战-对象的共享
- Java 并发之共享对象
- Java并发之对象共享
- Java并发编程详解之 线程安全和对象共享
- java并发编程实践之共享对象学习笔记
- 并发之对象的共享
- java并发编程 第三节 对象的共享
- 《Java并发编程实战》第三章 对象的共享 读书笔记
- 《Java并发编程实战》---线程安全性---对象的共享
- java并发编程(二)对象的共享
- Java并发编程实战笔记(二):对象的共享
- AIR NativeProcess 调用bat
- poj 1046
- 网络爬虫,获取页面图片
- 【map&list】int型数据处理之 桶放置
- 建立完全二叉树(对与错)c
- Java 并发编程之对象的共享
- Android Http请求方法汇总
- PCB导线宽度与截流能力的规定
- python删除指定文件夹下的文件
- IOS 点击空白处隐藏键盘的几种方法
- HDU 4619 Warm up 2 (二分图最小覆盖集)
- qt creator设置qmake路径(No valid Qt version set. Set one in Tools/Options)
- Android JNI使用例子
- Mvc4 WebApi 返回 格式