Java_多线程_多线程使用Vector或HashTable

Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本,这些概念我们都很清楚,也被前辈嘱咐过很多次,但我们经常会逃避使用Vector和HashTable,因为用得少,不熟嘛!只有在真正需要的时候才会想要使用它们,但问题是什么时候算真正需要呢?我们来看一个例子,看看使用线程安全的Vector是否可以解决问题,代码如下:

package deep;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Client {

    public static void main(String[] args) {
        // 火车票列表
        final List<String> tickets = new ArrayList<String>(100000);
        // 初始化票据池
        for (int i = 0; i < 100000; ++i) {
            tickets.add("火车票" + i);
        }
        // 退票
        Thread returnThread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    tickets.add("车票" + new Random().nextInt());
                }
            }
        };
        // 售票
        Thread saleThread = new Thread() {
            @Override
            public void run() {
                for (String ticket : tickets) {
                    tickets.remove(ticket);
                }
            }
        };
        // 启动退票线程
        returnThread.start();
        // 启动售票线程
        saleThread.start();
    }
}

模拟火车站售票程序,先初始化一堆火车票,然后开始出售,同时也有退票产生,这段程序有没有问题?可能会有读者看出了问题,ArrayList是线程不安全的,两个线程访问同一个ArrayList数组肯定会有问题。
没错,确定有问题,运行结果如下:
Exception in thread “Thread-1” java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at deep.Client$2.run(Client.java:29)

运去好的话,该异常马上就会报出。也许有人会说这是一个典型错误,只须把ArrayList替换成Vector即可解决问题,真的是这样吗?我们把ArrayList替换成Vector后,结果照旧,任然抛出相同的异常,Vector已经是线程安全的了,为什么还报这个错误呢?
这是因为混淆了线程安全和同步修改异常,基本上所有的集合类都有一个叫做快速失败(Fail-Fast)的校验机制,当一个集合在被多个线程修改并访问时,就可能会出现ConcurrentModificationException异常,这是为了确保集合方法一致而设置的保护措施,它的实现原理就是我们经常提到的modCount修改计数器:如果在读列表时,modCount发生变化(也就是有其他线程修改)则会抛出ConcurrentModificationException异常。这与线程同步是两码事,线程同步是为了保护集合中的数据不被脏读、脏写而设置的,我们来看线程安全到底用在什么地方,代码如下:

package deep;

import java.util.ArrayList;
import java.util.List;

public class Client {

    public static void main(String[] args) {
        // 火车票列表
        final List<String> tickets = new ArrayList<String>(100000);
        // 初始化票据池
        for (int i = 0; i < 100000; ++i) {
            tickets.add("火车票" + i);
        }
        // 10个窗口售票
        for (int i = 0; i < 10; ++i) {
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        System.out.println(Thread.currentThread().getId()
                                + "--" + tickets.remove(0));
                        try {
                            sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }
    }
}

还是火车站售票程序,有10个窗口在卖火车票,程序打印出窗口号(也就是线程号)和车票编号,很快我们就会看到这样的输出:
10–火车票0
9–火车票0
11–火车票1
13–火车票2
12–火车票3
15–火车票6
17–火车票5
16–火车票7
14–火车票7

注意看,上面有两个线程在卖同一张火车票,这才是线程不同步的问题,此时把ArrayList修改为Vector即可解决问题,因为Vector的每个方法前都加上了synchronized关键字,同时只会允许一个线程进入该方法,确保了程序的可靠性。
虽然在系统开发中我们一再说明,除非必要,否则不要使用synchronized,这是从性能的角度考虑的,但是一旦涉及多线程时(注意这里说的是真正的多线程,不是并发修改的问题,比如一个线程增加,一个线程删除,这不属于多线程的范畴),Vector会是最佳选择,当然自己在程序中加synchronized也是可行的方法。
HashMap的线程安全类HashTable与此相同,不再赘述。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值