几个程序解惑java多线程

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集合中的元素进行拷贝,只是拷贝了引用。否则的话,会有更多的问题。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值