Java 中的Sychronized关键字 和线程
2015-06-12 21:38
495 查看
这两天看到<<Thinking in java>> 多线程,里面有个syncrhonized 关键字,以前也稍微接触过一点。
好像挺令人迷惑的。
写了个小程序测试了一下:
程序中,通过getValue()观察i 的变化,来看线程 和Sychronized 关键字之间是怎么工作的。evenIncrement()保证i 是偶数,当i为奇数时,打印 i 的值,并退出。
运行结果为:
Got I!0
Got I!5234
Got I!6662
Got I!7647
5999 i = 7521 8088
有意思的是getValue()得到的i 可以不是偶数,否则就不会打印出5999这个奇数。
按道理不是的啊,evenIncrement() 每次都是让变量自增2的呀,i不可能是奇数的。可结果明明就打印出了奇数。
惟一的可能是当evenIncrement()中执行第一个i++时,getValue()获得了i的值。
那Sycrhonized 关键字是干什么的?
应该这样理解: 加了 Sycrhonized 关键字的方法体,里面的元素是“锁着的”,同时该方法只有获得钥匙(可能是java 里的某些权限)才能访问里面的资源(比如这里的 i),而且钥匙只有一把,一次只能由一个方法获得。
举个栗子: 所谓“一山不容二虎”,“一个花果山只能有一个美猴王”。
这里的 i 就相当于 “花果山”,两个美猴王就是加了Syncrhonized的方法。在同一个时间内,只有一个 猴王统治花果山。只有等一个猴王被赶走了,下一个美猴王才 能来。
后来,(另一个方法getValue() )沙僧也来了。沙僧怎么能进花果山啊?沙僧说:“我又不是声明了Sycrhonized 的方法,当然随时都可以来(访问i 的数值)了。”
也就是说getValue()方法可以随时访问 i,与evenIncrement ()是否完成i++两次操作没有任何关系。于是,打印出的i 就有可能是奇数。
那么,当我们把evenIncrement ()加了Sychronized 之后会怎么样了?答案是:没有输出,程序永远执行下去。这是显而易见的。
输出结果还有一个有意思的现象,可以帮助我们理解线程的一些小原理。
为什么结果往往不是 1而是其他的,比如5999,按道理说第一次getValue(),应该是1才对啊。
这就是cpu分配时间片造成的,当程序执行到evenIncrement()时,cpu有时候给它很多时间,它执行了不只一次,所以通常不是1。
而且
当执行 输出val 后,Cpu也许把时间分配给了evenIncrement(),这样此时i 完成了很多次自加,它的值就不会等于val 的值。
好像挺令人迷惑的。
写了个小程序测试了一下:
程序中,通过getValue()观察i 的变化,来看线程 和Sychronized 关键字之间是怎么工作的。evenIncrement()保证i 是偶数,当i为奇数时,打印 i 的值,并退出。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AtomTest implements Runnable { private int i = 0; public int getValue(){System.out.println("Got I!"+ i);return i ;} private synchronized void evenIncrement (){i++;i++;} /** * @param args */ public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); AtomTest at = new AtomTest(); exec.execute(at); while(true){ int val = at.getValue(); if(val %2!=0){ System.out.println(val+ " i = "+at.getValue()+" "+at.getValue() ); System.exit(0); } } } @Override public void run() { while(true){ evenIncrement(); } } }
运行结果为:
Got I!0
Got I!5234
Got I!6662
Got I!7647
5999 i = 7521 8088
有意思的是getValue()得到的i 可以不是偶数,否则就不会打印出5999这个奇数。
按道理不是的啊,evenIncrement() 每次都是让变量自增2的呀,i不可能是奇数的。可结果明明就打印出了奇数。
惟一的可能是当evenIncrement()中执行第一个i++时,getValue()获得了i的值。
那Sycrhonized 关键字是干什么的?
应该这样理解: 加了 Sycrhonized 关键字的方法体,里面的元素是“锁着的”,同时该方法只有获得钥匙(可能是java 里的某些权限)才能访问里面的资源(比如这里的 i),而且钥匙只有一把,一次只能由一个方法获得。
举个栗子: 所谓“一山不容二虎”,“一个花果山只能有一个美猴王”。
这里的 i 就相当于 “花果山”,两个美猴王就是加了Syncrhonized的方法。在同一个时间内,只有一个 猴王统治花果山。只有等一个猴王被赶走了,下一个美猴王才 能来。
后来,(另一个方法getValue() )沙僧也来了。沙僧怎么能进花果山啊?沙僧说:“我又不是声明了Sycrhonized 的方法,当然随时都可以来(访问i 的数值)了。”
也就是说getValue()方法可以随时访问 i,与evenIncrement ()是否完成i++两次操作没有任何关系。于是,打印出的i 就有可能是奇数。
那么,当我们把evenIncrement ()加了Sychronized 之后会怎么样了?答案是:没有输出,程序永远执行下去。这是显而易见的。
输出结果还有一个有意思的现象,可以帮助我们理解线程的一些小原理。
为什么结果往往不是 1而是其他的,比如5999,按道理说第一次getValue(),应该是1才对啊。
这就是cpu分配时间片造成的,当程序执行到evenIncrement()时,cpu有时候给它很多时间,它执行了不只一次,所以通常不是1。
而且
System.out.println(val+ " i = "+at.getValue()+" "+at.getValue() );val 的输出值和 at.getValue()+" "+at.getValue() 都不相同,而且两个getValue()获得的值通常也不相同,这就更说了CPU 分配时间的不固定。
当执行 输出val 后,Cpu也许把时间分配给了evenIncrement(),这样此时i 完成了很多次自加,它的值就不会等于val 的值。
相关文章推荐
- Java ConcurrentModificationException 异常分析与解决方案
- java笔记06 数组
- SpringMVC输入校验
- Spring格式化注解
- java中HashMap重要性质和优化总结
- JAVA线程
- OGNL表达式struts2标签“%,#,$”的区别
- java 文件读写工具 FileUtil
- java DOM 操作xml
- Myeclipse安装Activiti
- Java知多少(90)菜单
- SpringMVC学习(三)
- 快速排序及优化(Java版)
- SpringMVC学习(二)
- Java并发编程--Fork/Join框架使用
- java学习之IO流2
- JDK安装成功了,环境变量也配置好了,测试代码也可以运行,但是打不开eclipse
- Java之多线程下载
- JavaWeb之Servlet
- ssh2. 相关配置文件