线程安全测试 ArrayList Collections.synchronizedList CopyOnWriteArrayList

原创 2019-05-30 17:08 阅读(1105)次

ArrayList是线程不安全的,因此在并发编程时,经常会使用Collections.synchronizedList与CopyOnWriteArrayList来替代ArrayList,接下来对这3种list进行线程安全测试,以免自己忘记

主要测试的是add,get,遍历并发时的情况,代码与结论如下:

package test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ListTest {
	private CountDownLatch cdladd = new CountDownLatch(100);
	private CountDownLatch cdlget = new CountDownLatch(100);
	private CountDownLatch cdlitr = new CountDownLatch(100);

	//并发情况下,add并发不报错,但会丢失数据,add和get并发报错java.lang.ArrayIndexOutOfBoundsException,遍历会报错:java.util.ConcurrentModificationException
//	public static List<Integer> list = new ArrayList();
	//并发情况下,add并发数据正常,遍历会报错:java.util.ConcurrentModificationException
	public static List<Integer> list = Collections.synchronizedList(new ArrayList());
	//并发情况下,add并发数据正常,遍历正常
//	public static List<Integer> list = new CopyOnWriteArrayList<>();
	
	public ListTest() {
		list.add(1);//默认一条数据
	}
	private class ThreadAddTest implements Runnable{
		@Override
		public void run() {		
			try {
				cdladd.await();
				list.add(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	private class ThreadGetTest implements Runnable{	
		@Override
		public void run() {
			try {
				cdladd.await();
				int size = list.size()-1;
				if(size >= 0 ) {
					list.get(size);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	private class ThreadItrTest implements Runnable{	
		@Override
		public void run() {
			try {
				cdlitr.await();
				for (Integer integer : list) {

				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
		}		
	}
	
	public void doAdd() {
		ExecutorService executor = Executors.newFixedThreadPool(100);  //创建线程池
		//执行1000000次
		for (int i = 0; i < 1000000; i++) {
			if(i % 10000==0) {
				System.out.println("doAdd"+i);
			}
			executor.execute(new ThreadAddTest());
			cdladd.countDown();
		}		
		executor.shutdown(); 		
		//同步是否执行	
		while(!executor.isTerminated()){//还在执行完
			//System.out.println("线程在执行中....");
		}
		System.out.println("完成1:"+list.size());//正常情况下size要等于上面的循环次数+1(默认有一条)
	}
	public void doGet() {
		ExecutorService executor = Executors.newFixedThreadPool(100);  //创建线程池
		//执行1000000次
		for (int i = 0; i < 1000000; i++) {
			if(i % 10000==0) {
				System.out.println("doGet"+i);
			}
			executor.execute(new ThreadGetTest());
			cdlget.countDown();
		}		
		executor.shutdown(); 
		//同步是否执行	
		while(!executor.isTerminated()){//还在执行完
			//System.out.println("线程在执行中....");
		}
		System.out.println("完成2");
	}
	public void doItr() {
		ExecutorService executor = Executors.newFixedThreadPool(100);  //创建线程池
		//执行100000次
		for (int i = 0; i < 100000; i++) {
			if(i % 10000==0) {
				System.out.println("doItor"+i);
			}
			executor.execute(new ThreadItrTest());
			cdlitr.countDown();
		}		
		executor.shutdown(); 
		//同步是否执行	
		while(!executor.isTerminated()){//还在执行完
			//System.out.println("线程在执行中....");
		}
		System.out.println("完成3");
	}
	public static void main(String[] args) {
		ListTest test = new ListTest();
    		Thread a = new Thread(new Runnable() {
			@Override
			public void run() {
				test.doAdd();
			}
		});
    		Thread b = new Thread(new Runnable() {
    			@Override
    			public void run() {
    				test.doGet();
    			}
    		});
    		Thread c = new Thread(new Runnable() {
    			@Override
    			public void run() {
    				test.doItr();
    			}
    		});
    		a.start();
    		b.start();
    		c.start();
	}
}


下一篇:docker中安装redis