您的位置:首页 > 编程语言 > Java开发

(29)21.3.3 原子性与易变性---Java编程思想之并发笔记

2012-12-31 15:42 507 查看
1.原子操作
原子操作时不能被线程机制中断的操作;一旦操作开始,那么一定可以在可能发生的“上下文切换”之前(切换到其它线程执行)执行完毕。
一般情况下不能用原代替同步,除非你是并发专家。否则就是在玩火。
“如果你可以编写用于现代未处理器的高性能JVM,那么就有资格去考虑是否可以避免同步”。
了解原子性是很有用的,原子性可以应用于除long和double之外的所有基本类型之上的“简单操作”。
2.volatile关键字
volatile关键字确保了应用中的可视性。如果将一个域申明为volatile的,那么只有对这个域产生了写操作,那么所有的读操作就斗可以看到这个修改。即使使用了本地缓存,情况也确实如此,volatile域会立即被写到主存中,而读取操作就发生在主存中。
如果一个域完全用sychronized方法或语句块来防护,那么就不必将其设置为volatile。
一个任务所作的任何写操作对这个任务说都是可视的,因此如果它需要在这个任务内部可视,那么就不需要将其设置为volatile。
档一个域的值依赖于它之前的值时,volatile就无法工作。如果某个域的值受到其它域的值的限制,那么volatile也无法工作
使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。
第一选择是synchronized关键字,这是最安全的方式。

例子1:
[align=left]package jiangning.c21;[/align]

[align=left]import java.util.concurrent.ExecutorService;[/align]
[align=left]import java.util.concurrent.Executors;[/align]

public class AtomicityTest implements Runnable
{
[align=left] [/align]
private int i =
0;
[align=left] public int getValue(){[/align]
[align=left] return i ;[/align]
[align=left] }[/align]
[align=left] private synchronized void evenIncrement(){[/align]
[align=left] i++;[/align]
[align=left] i++;[/align]
[align=left] }[/align]
[align=left] [/align]
public void run()
{
[align=left] while(true ){[/align]
[align=left] evenIncrement();[/align]
[align=left] }[/align]
[align=left] }[/align]

public static void main(String[]
args) {
[align=left] ExecutorService exec = Executors. newCachedThreadPool();[/align]
[align=left] AtomicityTest at = new AtomicityTest();[/align]
[align=left] exec.execute(at);[/align]
[align=left] while(true ){[/align]
int val
= at.getValue();
if(val
%2 != 0){
System. out.println("val
= " + val);
[align=left] System. exit(0);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left]}[/align]

[align=left]/**[/align]
[align=left]val = 9[/align]
[align=left]*/[/align]
[align=left]分析:尽管return确实是原子性操作,但是缺少同步使得其数值可以在处于不稳定的中间状态被读取。此外由于i也不是volatile的,因此还存在可视性问题。[/align]

[align=left]例子2.[/align]

[align=left]package jiangning.c21;[/align]

public class SerialNumberGenerator
{

private static volatile int serilNumber =
0;
[align=left] public static int nextSerialNumber(){[/align]
[align=left] return serilNumber ++;[/align]
[align=left] }[/align]
[align=left]}[/align]

[align=left]package jiangning.c21;[/align]

[align=left]import java.util.concurrent.ExecutorService;[/align]
[align=left]import java.util.concurrent.Executors;[/align]
[align=left]import java.util.concurrent.TimeUnit;[/align]

[align=left]class CircularSet{[/align]
[align=left] private int [] array;[/align]
[align=left] private int len ;[/align]
private int index =
0;
[align=left] public CircularSet( int size){[/align]
[align=left] array = new int[size];[/align]
len =
size;
for (int i=0;
i<size; i++){
array [i]
= -1;
[align=left] }[/align]
[align=left] }[/align]
[align=left] public synchronized void add( int i){[/align]
array [index ]
= i;
index =
++index % len;
[align=left] }[/align]
[align=left] public synchronized boolean contains( int val){[/align]
for (int i=0;
i< len; i++){
if (array [i]
== val){
[align=left] return true ;[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] return false ;[/align]
[align=left] }[/align]
[align=left]}[/align]
public class SerialNumberCheck
{
private static final int SIZE =
10;
[align=left] private static CircularSet serials = new CircularSet(1000);[/align]
private static ExecutorService exec =
Executors.newCachedThreadPool();
[align=left] static class ServialCheck implements Runnable{[/align]
[align=left] public void run(){[/align]
[align=left] while (true ){[/align]
int serial
= SerialNumberGenerator.nextSerialNumber();
[align=left] if (serials .contains(serial)){[/align]
System. out .println("Duplicate:
" + serial);
[align=left] System. exit(0);[/align]
[align=left] }[/align]
[align=left] serials .add(serial);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] }[/align]
public static void main(String[]
args) throws Exception {
for (int i=0;
i < SIZE; i++){
[align=left] exec .execute( new ServialCheck());[/align]
[align=left] }[/align]
if (args.length >
0) {
[align=left] TimeUnit. SECONDS .sleep( new Integer(args[0]));[/align]
System. out .println("No
duplicates detected" );
[align=left] System. exit(0);[/align]
[align=left] }[/align]
[align=left] }[/align]

[align=left]}[/align]

[align=left]这个例子说明最安全的方法是遵循Brian的同步规则[/align]
[align=left]总结:本节告诉我们在进行同步的时候要使用synchronized关键字,而不要依赖原子性,因为你不专家。[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: