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

Java Notes: Java的多线程创建的两种方法以及Race Condition的解释

2016-10-17 05:57 246 查看
理解Java多线程的关键是理解好job-worker模式。

job指的是具体一项工作的内容,譬如,搅拌水泥这件事情。可以找很多个worker来做。在Java当中,job就是runnable类,而thread类则是worker。

线程的创建:

所以,比较提倡的创建多线程的方法是:

(1) 首先是创建一个job的内容(Runnable类)

public MyRunnable implements Runnable{
/*可以有类成员,通过是其它类的引用,方便调用其它类的方法*/
public MyRunnable(){

}//或者创建其它的构造器,为了初始化类成员
@Override
public void run(){
/*需要做的Job在这里定义,可以调用类成员引用所指向的类的方法*/
}
}


Runnable是一个接口,所以事先这个接口需要为这个接口的唯一一个抽象方法赋予实际内容。

(2)然后,再通过这个runnable的实现类去创建不同的线程(worker):

Thread thread_one = new Thread(new MyRunnable());
Thread thread_two = new Thread(new MyRunnable());
thread_one.start();
thread_two.start();


刚刚说了上述方法是比较提倡的方法,那么说还有不提倡的方法咯?

是的,因为Thread类实际上是Runnable的实现类。所以,它也有自己的一个run方法。我们可以通过继承Thread来改写其run()方法来定义Thread具体的内容。

public MyThread extends Thread{
/*只能自己定义类成员,不能再通过继承其它的类获得类成员*/
public MyThread(){
/*初始化类成员,定义其它形式的构造器可以传入其它类的对象*/
}
@Override
public void run(){
/*定义job*/
}
}


为什么不提倡呢?虽然上述的方法省掉了一个runnable的具体类对象,但是它没办法通过继承其它类获得对应的类成员。而且,job-worker的模式在runnable/ thread的定义方法中完美体现出来,而在这里却被混为一体了。当我们为同一个job创建多个线程的时候,还是runnable、thread这种定义比较方便。

Race Condition:

这里需要注意一点,MyThread.start()、thread_1.start()(如果直接调用Thread类的run或者Runnable的run则还是在同一个线程执行)只是告诉JVM把我这个线程加入调度队列当中,并不代表该线程会马上执行。事实上,在贯穿整个多线程编程当中,需要谨记住一点:You never know a given may or may not be running at anytime。

因为这个原因,某个线程可能因为JVM线程调度的原因,就停在了job中间的某句话执行之后。而另一个线程就开始执行了。而因为这样的现象存在,所以对于多个线程共享同一个对象的堆空间的时候,很有可能出现race condition。

举个例子【1】:



上述代码如果在两个线程同时执行的,可能存在这么一种情况:



一开始,Thread1首先执行完if,判断条件成立后,进入if体,准备执行添加语句时,被强制停止了。然后Thread 2开始执行,这时候集合还是没有元素的,所以thread2成功执行了所有语句后,thread1又继续执行,这时候,thread1因为已经进入了if语句后,直接又往集合添加元素。因为这样的原因,使得代码的执行效果和我们设计逻辑不符。

这种情况有个专业的名称,称为race condition。

Reference:

[1] Terry Lee <Java Development>

[2] https://segmentfault.com/a/1190000003810166
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息