Java并发集合操作中对锁的应用

本文通过示例代码展示了如何使用ReentrantReadWriteLock解决多线程环境下读取和修改集合时产生的并发问题,避免了ConcurrentModificationException异常。
摘要: 通过锁解决在不同线程读取和修改集合引发的问题。
下面以List结合为例子,
先来看以下代码:
public static ArrayList<String>datas=new ArrayList<String>();
//初始化数据
public static void initData(){
for(int i=0;i<20;i++){
datas.add(""+i);
}
}

//线程1,读取集合的数据
public static  Thread thread1=new Thread(){
public void run() {
//int size=datas.size();
for(String data:datas){
System.out.println(data);
}
};
};
//线程2,删除集合的数据
private static Thread thread2=new Thread(){
public void run() {
int size=datas.size();
for(int i=0;i<size;i++){
datas.remove(0);
System.out.println("remove");
}
};
};
//启动程序
        public static void main(String[] args) {
initData();
thread1.start();
thread2.start();
}




这样子运行的话,肯定会出一个异常。下面请看执行的结果 
remove 
remove 

remove 
remove 
remove 
remove 
remove 
remove 
Exception in thread "Thread-0" remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
java.util.ConcurrentModificationException 
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819) 
at java.util.ArrayList$Itr.next(ArrayList.java:791) 


在现实编程环境中,经常会遇到既需要遍历集合,又需要在另一个线程中删除集合。那么怎么解决呢?
下面就来解决这个问题,我们用到了位于java.util.concurrent.locks包里面的ReentrantReadWriteLock;
简称读写锁,它有两个核心的方法,分别是readLock()和writeLock(),即获取读锁和写锁。获取读锁的前提是写锁没有锁住。获取写锁的前提是读锁没有锁住。也就是保证在读的时候就没有人在写数据或者修改数据。在写数据或者修改数据的时候就没有人在读数据。就好像我们编辑word文档一样,在编辑状态,就不允许移动,复制。根据这个原理,我们接下来改进代码如下:
public static final ReadWriteLock lock = new ReentrantReadWriteLock(false);

public static ArrayList<String>datas=new ArrayList<String>();
public static void initData(){
for(int i=0;i<20;i++){
datas.add(""+i);
}
}


public static  Thread thread1=new Thread(){
public void run() {
lock.readLock().lock();
for(String data:datas){
System.out.println("t1  "+data);
}
lock.readLock().unlock();
};
};
public static  Thread thread3=new Thread(){
public void run() {
lock.readLock().lock();
for(String data:datas){
System.out.println("t3  "+data);
}
lock.readLock().unlock();
};
};



private static Thread thread2=new Thread(){
public void run() {
int size=datas.size();
lock.writeLock().lock();
for(int i=0;i<size;i++){
datas.remove(0);
System.out.println("remove");
}
lock.writeLock().unlock();
};
};

public static void main(String[] args) {
initData();
thread1.start();
thread2.start();
thread3.start();
}




这时候程序执行结果是:
t1  0
t1  1
t1  2
t1  3
t1  4
t1  5
t1  6
t1  7
t1  8
t1  9
t1  10
t1  11
t1  12
t1  13
t1  14
t1  15
t1  16
t1  17
t1  18
t1  19
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove
remove


大家可能疑惑为啥没有t3的打印呢?因为读锁在释放之后,立马就被写锁占用了,写锁的线程把集合清空了,所以当轮到线程3的时候就没有数据了,多试几次,会发现还有一种执行结果就是全部都是remove。没有任何打印,这是因为先执行了线程2的缘故。如果我们按照这样的顺序执行,又会不同:
public static void main(String[] args) {
initData();
thread1.start();
thread3.start();
thread2.start();
}




t3  0 
t1  0 
t3  1 
t1  1 
t3  2 
t1  2 
t3  3 
t1  3 
t3  4 
t1  4 
t3  5 
t1  5 
t3  6 
t1  6 
t3  7 
t1  7 
t3  8 
t1  8 
t3  9 
t1  9 
t3  10 
t1  10 
t3  11 
t1  11 
t3  12 
t1  12 
t3  13 
t1  13 
t3  14 
t1  14 
t3  15 
t1  15 
t1  16 
t1  17 
t3  16 
t1  18 
t1  19 
t3  17 
t3  18 
t3  19 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 
remove 


可以看到读锁在不同的线程中是不排斥的。好的,就为大家介绍到这里。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值