`
xidajiancun
  • 浏览: 458329 次
文章分类
社区版块
存档分类
最新评论

在Java中进行线程同步

 
阅读更多

大家都 知道一个运行着的程序就是一个进程,而进程之间的内存空间是相互独立的,所以两个进程只要不存在操作同一公共资源(如同时操作同一个文件)的情况,也就不 会存在进程安全的问题。但线程就不一样了,在同一个进程中的不同线程是完全共享资料的,他们可以访问同一个内存区域,所以也就有一个多个线程同时读写一个 内在地址(或关联数据)时产生的不可预期的问题了,这就是所谓的线程不安全。如果有下面的程序:

public class GetID {


private static int id = 0;


public static int getId() {

id = id + 1;

return id;

}

}

从程序我们可以知道,类GetID的功能主要是分配一个唯一的ID号,但代码id = id + 1在多线程运行时可能会有冲突的情况,可能两个线程同时取得到了id然后再加一,即可能同时取得到相同的id值,虽然这种问题发生的可能性极低,但一但发生可能会产生非常严重的问题。

现在的程序都应该是多线程的,特别是CPU已经多核化了,所以在进行编程时需要特别的注意线程安全性。如果你学习过操作系统的知道就会知道p操作和v操作了,p操作是进入线程同步(就是对同一段程序不能有两个线程进入),而v操作是退出线程同步。但使用p操作和v操作需要注意在异常时仍然需要进行v操作,比如下面的代码:

p(); // 开始线程同步

try {

// TODO ... do something ...

} finally {

v(); // 退出线程同步

}

当然在Java中就不需要如此麻烦了,因为并不是每一个程序员都会十分注意地会将v操作放到finally段中,所以Java当中提供了一种更好的同步方法:

public static class SyncClass {

private static int uid = 0;

private static Object syncObject = new Object();


public static int getUid() {

synchronized (syncObject) {

return uid++;

}

}

}

类似于上面的代码,在Java中使用关键字synchronized来进行同步,这种方法非常直观而且很安全,即使程序员没异常处理的意识也不会引起问题。在上面的代码中,程序通过对对象syncObject来进行同步操作,也就是说只要是使用syncObject对象的同步代码即使在多个线程中也只会一个线程能够进行操作。

下面的用法也可以进行线程同步:

public static class SyncClass2 {

private static int uid = 0;


public static synchronized int getUid() {

return uid++;

}


public static int getUid2() {

synchronized (SyncClass2.class) {

return uid++;

}

}

}

或是使用下面的方法进行同步:

public static class SyncClass3 {

public synchronized int getIntItem() {

return 0;

}


public int getIntItem2() {

synchronized (this) {

return 0;

}

}

}

但需要注意的是上面的两个同步方法是不一致的,一个是对类本身进行同步,另一个是对this对象进行同步,所以这两个方法请不要同时使用,因为这两个方法同时使用会非常容易引起误会(让人误解为一个static函数的同步方法会和一个普通的同步方法进行真正的同步),就如下面的程序:

public static class SyncClass4 {

public static synchronized int getIntItem() {

return 0;

}


public synchronized int getIntItem2() {

return 0;

}

}

在上面的程序中,方法getIntItem和方法getIntItem2并不会进行同步,因为一个是static方法,而另一个不是。

更多的选择进行线程同步:Lock,Lock是一个非常有用的Java接口,类似于p、v操作来进行线程同步,下面是一种Lock的使用方法:

public static class SyncClass5 {

private static int uid = 0;

private static Lock lock = new ReentrantLock();


public static int getUid() {

lock.lock();

try {

return uid++;

} finally {

lock.unlock();

}

}

}

这里的lock及unlock方法都类似于p操作及v操作,所以也需要注意将unlock方法放到finally段中去。这种同步的方法很好但有些场合下就不是很合适了,比如:

public static class SyncClass6 {

private static Lock lock = new ReentrantLock();


public static int readInt() {

lock.lock();

try {

return 0;

} finally {

lock.unlock();

}

}


public static void writeInt(int value) {

lock.lock();

try {

// write value

} finally {

lock.unlock();

}

}

}

因为在多个线程读的时候也只有一个线程能进入读的区域,这样影响了读的性能,会使程序变慢。下面是另一个更好些的写法:

public static class SyncClass7 {

private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();


public static int readInt() {

readWriteLock.readLock().lock();

try {

return 0;

} finally {

readWriteLock.readLock().unlock();

}

}


public static void writeInt(int value) {

readWriteLock.writeLock().lock();

try {

// write value

} finally {

readWriteLock.writeLock().unlock();

}

}

}

因为多个readInt方法可以由多个线程同时进入,这样可以让程序跑地更快。

关于多线程的知识还有很多很多,大家可以参看这方面的书籍或资料,去看看Java的相关的源代码也非常有益,应该能学到很多很多东西。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/jht5945/archive/2008/04/08/2260198.aspx

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics