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

并发编程 专栏收录该内容
10 篇文章 0 订阅

我们知道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
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论
请先登录 后发表评论~
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

打赏作者

沉默andy

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值