public class Test implements Runnable
{
Thrd thd;
Test2(Thrd thd){
this.thd = thd;
}
public void run(){
while(true){
int v = thd.getNext();
System.out.println(Thread.currentThread().getName()+" : "+v);
try{Thread.sleep(1000);}catch(Exception e){}
}
}
public static void main(String args[]){
new Thread(new Test(new Thrd())).start();
new Thread(new Test(new Thrd())).start();
}
}
class Thrd
{
private int value = 0;
public synchronized int getNext(){
return value++;
}
}
这里在类Thrd中getNext方法使用了synchronized,也就是说这是一个同步方法,那么Test的main方法中,返回的value是不是会不同呢。运行程序(这里用了死循环)发现两个线程之间并没有同步,为什么呢?
原因很简单对于每个线程,我们传递了不同的Thrd类的实例,每个实例都有自己的getNext方法,二者之间是不同的。
现在我们把Thrd类做如下修改:
class Thrd
{
private static int value = 0;
public synchronized static int getNext(){
return value++;
}
}
运行程序,发现现在value在两个线程之间实现了同步。而原因就是现在getNext方法成为了类方法,两个线程访问的是同一个方法实例,所以可以同步。
现在给出一个复杂一点的程序(论坛上找的),我们可以按照上面的思路分析了。
import java.util.*;
public class Test extends Thread{
private String st;
private static List<String> list = new ArrayList<String>();
public Test(int i) {
st = "str" + i;
}
public static void main(String[] args) throws Exception {
String[] arr = new String[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = "str" + i;
list.add(arr[i]);
}
for (int i = 0; i < arr.length; i++) {
Test test = new Test(i);
test.start();
}
while(true){
if (list.isEmpty()) {
break;
}
sleep(500);
}
System.out.println("finished");
}
public void run() {
removeStr(st);
System.out.println(st);
}
public synchronized static void removeStr(String str) {
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String strTemp = it.next();
if (strTemp.equals(str)) {
it.remove();
break;
}
}
}
public synchronized static boolean isEmpty() {
return list.isEmpty();
}
}
现在程序变得复杂了,而且由于包含了对线程非安全的list的操作,难度增大了。
这个程序是正确的,假如现在把方法removeStr和isEmpty前面的static去掉(如前所述,这时方法变为每个程私有的),程序可能会出现3中运行结果。下面我们一一分析。
首先需要说明的是,Test test = new Test(i); test.start();从这两句可知,第i个线程st=“stri”;线程start之后,每个线程从list中寻找元素跟stri相同的元素,并将其删除。其中,list是static的,每个线程共享。
1.运行正常结束。
这取决于一种特定的线程执行顺序,就是每个线程在对list进行操作的时候没有形成竞争条件,最后恰好删除list中所有元素,main函数中list.isEmpty()为真,循环结束,main也随之结束。
2.死循环。
这个是因为--size(通过remove源码可以知道)时,被另外一个线程抢占并同样进行了--size的操作。(考虑每一个线程的工作栈,事先会对size进行拷贝,size被另外一个线程改变后,上一个线程看到的还是旧的size值)
3.抛出ConcurrentModificationException。
原因是其中一个线程在it.next()与it.remove()之间被另外一个线程抢占并执行进行了删除操作。
而且还可以看到,每个线程的工作栈中并没有对list集合中的元素进行拷贝,只是拷贝了引用。否则的话,会有更多的问题。