posts - 6,  comments - 0,  trackbacks - 0

多线程的共享资源
    
java提供了关键字synchronized的形式,为防止资源冲突提供内置的支持。每个对象都有一个单一的锁,这个锁是存在与对象内部的,当你调用了一个对象的某个synchronized方法,这个对象中的其他的synchronized方法必须要等到先前的那个方法执行完毕,释放了锁以后才可以被调用。一个线程可以多次获得对象的锁,JVM负责跟踪对象被加锁的次数。显然,只有首先获得了锁的线程才能允许继续获取多个锁。每当线程离开了一个synchronized方法,计数减少,直到零,锁被释放。首先,我们来看一个线程访问的冲突问题。以下是thinking in java上的一个例子。

 1public class AlwaysEven {
 2    private int i;
 3    public void next() {i++; i++;}
 4    public int getValue() {return i;}
 5    /**
 6     * @param args
 7     */

 8    public static void main(String[] args) {
 9        // TODO Auto-generated method stub
10        final AlwaysEven ae = new AlwaysEven();
11        new Thread("Watcher"{
12            public void run() {
13                while (true{
14                    int val = ae.getValue();
15                    System.out.println(val);
16                    if (val % 2 !=0{
17                        System.exit(0);
18                    }

19                }

20            }

21        }
.start();
22        while(true
23            ae.next();
24    }

25
26}

27


        next()函数将i递增两次。getValue()负责取得i的值。我的理想是每次返回的值都应该是偶数,但结果是(我电脑的某次运行结果):
10863560
11452996
11452996
11452996
11452996
11452996
11452996
11452996
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11453546
11836542
11862704
11898351
    结果的最后出现了奇数,出现这种情况的原因是,当next()函数只执行了第一次递增以后就被切换出处理器,因此出现了期望之外的数据,这就是多线程带给我们的困扰。在这个程序中危险的共享资源就是 i。为了解决这个问题,我们就可以使用synchronized关键字。
 1public class AlwaysEven {
 2    private int i;
 3    public synchronized void next() {i++; i++;}
 4    public synchronized int getValue() {return i;}
 5    /**
 6     * @param args
 7     */

 8    public static void main(String[] args) {
 9        // TODO Auto-generated method stub
10        final AlwaysEven ae = new AlwaysEven();
11        new Thread("Watcher"{
12            public void run() {
13                while (true{
14                    int val = ae.getValue();
15                    System.out.println(val);
16                    if (val % 2 !=0{
17                        System.exit(0);
18                    }

19                }

20            }

21        }
.start();
22        while(true
23            ae.next();
24    }

25
26}
        我们对next()和getValue()函数进行了同步处理。这个程序将无休止的运行下去,因为你再也不会得到一个奇数了。
需要注意的是凡是访问需要保护的共享资源的方法都要进行同步处理,如果getValue()方法没有同步的话,结果依然会混乱,因为它将不受到锁的限制。

原子操作
        
原子操作是指不能被线程调度机制中断的操作,它一定会在线程切换之前完成。对除了long和double以外的基本类型,对这种变量进行简单的赋值或者返回操作的时候,才算是原子操作。之所以除去long和double类型,是因为long和double类型比其他基本类型要大,所以JVM不能把对它的读取或赋值当成是单一原子操作。然而,你只要给long和double加上volatile,操作就是原子的了。需要注意的是JVM中的自增加操作并不是原子的。

临界区
    
如果你希望同步部分代码,而不是整个方法,可是使用synchronized进行:
1synchronized (yourObject) {
2    
3}
    在进入这个临界区前必须先得到yourObject的对象锁。同步控制块必须指定一个对象才能进行同步,通常,最合理的对象就是在其上调用方法的当前对象:synchronized(this). 下面有一个例子:
 1
 2class Mysync {
 3    private Object syncObject = new Object();
 4    
 5    public synchronized void syn1() {
 6        System.out.println("I get in syn1");
 7        try {
 8            Thread.sleep(5000);
 9        }
 catch (InterruptedException e) {
10            // TODO Auto-generated catch block
11            e.printStackTrace();
12        }

13        System.out.println("I leave syn1");
14    }

15    
16    public void syn2() {
17        System.out.println("I get in syn2");
18        synchronized(syncObject) {
19            try {
20                Thread.sleep(5000);
21            }
 catch (InterruptedException e) {
22                // TODO Auto-generated catch block
23                e.printStackTrace();
24            }

25        }

26        System.out.println("I leave syn2");
27    }

28}

29public class TestSync {
30
31    /**
32     * @param args
33     */

34    public static void main(String[] args) {
35        // TODO Auto-generated method stub
36        final Mysync s = new Mysync();
37        new Thread() {
38            public void run() {
39                s.syn1();
40            }

41        }
.start();
42        s.syn2();
43    }

44
45}
运行结果是:(您的机器可能有不同结果)
I get in syn2
I get in syn1
I leave syn1
I leave syn2
    这个结果告诉我们如果如果两个同步方法如果不是针对同一个对象,即使一个对象是另一个的对象的内部变量,它们将互不影响。
posted on 2008-03-27 17:17 piggytommy 阅读(206) 评论(0)  编辑  收藏 所属分类: Java Basic

只有注册用户登录后才能发表评论。


网站导航: