【IT168技术】要真正理解如何解决线程资源共享冲突的问题,还真有点复杂,但是这个又是线程的精华所在,也是线程中最重要的知识,我要尽力讲清楚它,因此内容比较多了。
相关阅读:
上篇的末尾我写了一段实例代码,想表现线程抢占资源时候所发生的资源冲突问题,不知道大家真的看明白了那段代码的意思吗?反正我对这段代码琢磨了半天才领悟了其中的含义。这里我还是先把前面那段代码贴出来:
package cn.com.sxia;
public class Semaphore implements Invariant {
private volatile int semaphore = 0;
public void acquire(){
++semaphore;
}
public boolean available(){
return semaphore == 0;
}
public void release(){
--semaphore;
}
@Override
public InvariantState invariant() {
int val = semaphore;
if (val == 0 || val == 1){
return new InvariantOK();
}else{
return new InvariantFailure(new Integer(val));
}
}
}
在main函数里我们创建了两个SemaphoreTester对象,也就是启动了两个线程,两个线程操作的是同一个Semaphore对象,换句话说操作的是同一个资源了,我们不断运行main函数监控程序总会打印出一次失败的信息,比如下面的信息:
我们再看看所写的代码,我们改写下程序,我们没有启动两个线程,只启动了一个Semaphore线程时候,SemaphoreTester里的run方法使得Semaphore里的semaphore的值总不会变成非0或1的数值,那么监控的程序也就不会报出我们失败信息来。那么到底是什么原因产生了失败了?
原因在于一个线程可能从对available()的调用中返回真,但是当此线程调用acquire()时候,第二个线程可能已经调用了acquire()并且增加或者减少了semaphore字段的值了,此时的InvariantWatcher就可能发现我们的数值违反了我们制定的约束,不能为非0或1的值导致程序被停止了。
大家还要注意:监控程序线程我调用了join()方法,大家还记得这个方法吗?我在前面的文章里面介绍过他,加上这个方法会让前面两个线程一直执行直到发生了失败才会调用我们写的监控程序,join方法让我们的监控程序不会干扰两个线程的正常运行。
另外对于volatile关键字,这个也不是一句两句说的清楚的,我在后面会做进一步的解释的。
上面的实例代码很好的说明了多线程里资源竞争的难题,我学到这里,个人觉得根本原因还是在线程调度机制CPU时间片的随机切换所导致的。
写上面的实例代码我提到这段代码是模仿信息量的程序的简化版,这个又怎么理解了?
前面我提到过简单“信号量”的概念,它可以看做两个线程之间的通讯标志对象。假如信号量的值为0,则信号量所监控的资源是可用的,如果非0,则监控资源不可用,那么其他线程就要等待了,当资源可以被线程使用时候,线程就会增加信号量的值,该线程然后继续执行并使用这个监控资源,但是其他线程是不能使用监控资源的。我们的例子就是按这个原理写出来的,属性semaphore的初始值是0,这就是说各个线程在初始化状态下都是被激活的,线程们的赶紧抢占资源啊。acquire方法就是给信号量增加值,release方法释放信号量的数值,available方法就是对信号量值进行判断,如果值为0,其他线程就可以抢占资源,不为零其他线程就得等待了。为了很好的演示我写的程序功能,同时程序里面还加入了我写好的监控程序的代码。