跟着实例学习java多线程8-同步容器类的问题

分类: 转载文章 0人评论 1年前发布

我们知道java有很多线程安全的容器类,我们也知道如果把可变状态的管理交给这些线程安全类来管理就可以实现线程安全,但是我们还可能遇到不可想象的问题。

例如:

package com.home.thread.thread8;
import java.util.Vector;
/**
 * @author gaoxu
 * 实践出真知!
 */
public class VectorQueue {
	private static VectorQueue instance;
	private static Vector<String> list;
	private VectorQueue(){
		 list = new Vector<String>();
	}
	public Vector<String> getVector(){
		return list;
	}
	public synchronized static VectorQueue getInstace() {
		if(instance==null){
			 instance = new VectorQueue();
		}
		return instance;
	}
	public String getLast(Vector<String> list){
		int index = list.size()-1;
		return (String) list.get(index);
	}
	public void delLast(Vector<String> list){
		int index = list.size()-1;
		list.remove(index);
	}
}

上面的代码中,两个方法都是在处理Vector类的对象,我们知道Vector是一个线程安全的类,那么这两个方法在多线程访问的时候可以得到正确的值得吗?让我们来做个实例验证一下。

我们写一个多线程调用的例子。

package com.home.thread.thread8;
import java.util.Vector;
/**
 * @author gaoxu 实践出真知!
 */
public class VectorMain {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		VectorQueue vq = VectorQueue.getInstace();
		for (int i = 0; i < 10; i++) {
			String test = new String("" + i);
			vq.getVector().add(test);
		}
		for (int j = 0; j < 10; j++) {
			ThreadTest threadTest = new ThreadTest();
			threadTest.start();
		}
	}
	private static class ThreadTest extends Thread {
		VectorQueue vq = VectorQueue.getInstace();
		public ThreadTest() {
		}
		public void run() {
			String data = (String) vq.getLast(vq.getVector());
			vq.delLast(vq.getVector());
			System.out.println(data);
		}
	}
}

让我们来看一下运行结果(这样的结果需要运行很多次可能才会得到,这就是Vector的问题,也是其他线程安全类的问题)

<pre class="html" name="code">9
8
7
6
5
4
3
2
Exception in thread "Thread-8" 1
java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 1
 at java.util.Vector.get(Vector.java:694)
 at com.home.thread.thread8.VectorQueue.getLast(VectorQueue.java:26)
 at com.home.thread.thread8.VectorMain$ThreadTest.run(VectorMain.java:40)

这绝对不是我们想要的结果,这说明发生竟态条件的问题,get和del的线程访问list.size的时候可能获得的是同一个值,这样获得同一个值的两个线程del操作完了再get操作就出现了如上的错误。

那我们该怎样修改才可以得到正确的值呢?那就是采用客户端加锁的方式,其实我们利用非线程安全的基础对象类时都会注意同步的问题,那么让我们来看实例。

package com.home.thread.thread8;
import java.util.Vector;
/**
 * @author gaoxu 实践出真知!
 */
public class VectorQueue {
	private static VectorQueue instance;
	private static Vector<String> list;
	private VectorQueue() {
		list = new Vector<String>();
	}
	public Vector<String> getVector() {
		return list;
	}
	public synchronized static VectorQueue getInstace() {
		if (instance == null) {
			instance = new VectorQueue();
		}
		return instance;
	}
	public  String getLast(Vector<String> list) {
		synchronized (list) {
			int index = list.size() - 1;
			return (String) list.get(index);
		}
	}
	public void delLast(Vector<String> list) {
		synchronized (list) {
			int index = list.size() - 1;
			list.remove(index);
		}
	}
}

我们使用客户端锁,运行结果如下,我们看到这样的运行结果本身就可以看出来线程执行的时序是完全无序的,同时也印证的锁的存在。

9
8
7
6
5
4
3
0
1
2

这种方式的加锁,我运行了很多遍,虽然没有出现Exception,但我总有种感觉,Vector不可信,哈哈!还有HashTable也不可信啊。

下一次我们来实践一下,ConcurrentHashMap!

以上实例中的代码还可以更优化:

package com.home.thread.thread8;
import java.util.Vector;
/**
 * @author gaoxu 实践出真知!
 */
public class VectorMain {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		VectorQueue vq = VectorQueue.getInstace();
		for (int i = 0; i < 10; i++) {
			String test = new String("" + i);
			vq.getVector().add(test);
		}
		for (int j = 0; j < 10; j++) {
			ThreadTest threadTest = new ThreadTest();
			threadTest.start();
		}
	}
	private static class ThreadTest extends Thread {
		VectorQueue vq = VectorQueue.getInstace();
		public ThreadTest() {
		}
		public void run() {
			String data = (String) vq.getLast();
			vq.delLast();
			System.out.println(data);
		}
	}
}
package com.home.thread.thread8;
import java.util.Vector;
/**
 * @author gaoxu 实践出真知!
 */
public class VectorQueue {
	private static VectorQueue instance;
	private static Vector<String> list;
	private VectorQueue() {
		list = new Vector<String>();
	}
	public Vector<String> getVector() {
		return list;
	}
	public synchronized static VectorQueue getInstace() {
		if (instance == null) {
			instance = new VectorQueue();
		}
		return instance;
	}
	public  String getLast() {
		synchronized (list) {
			int index = list.size() - 1;
			return (String) list.get(index);
		}
	}
	public void delLast() {
		synchronized (list) {
			int index = list.size() - 1;
			list.remove(index);
		}
	}
}
上一篇:
下一篇:
0 条评论