本篇博文主要是初步试用并测试ConcurrentHashMap等并发容器的并发
主要分为两部分测试
第一部分测试ConcurrentHashMap和HashMap区别
第二部分测试ConcurrentHashMap的并发能力(利用多线程,定时器测试)
测试代码1:
package test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapTest {
public static void main(String[] args) {
//测试ConcurrentHashMap能不能put进去value为null的键值对
// ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
// map.put("key1",null);//不能put进去null
// System.out.println(map);
//测试HashMap能不能put进去value为null的键值对
// map1.put(null, null);
//测试HashMap的迭代器
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "1");
myMap.put("3", "1");
Iterator<String> it=myMap.keySet().iterator();
while(it.hasNext()){
String key=it.next();
if(key.equals("2")){
myMap.put(key+"new", "newvalue");
}
}
System.out.println(myMap);
//测试ConcurrentHashMap的迭代器
ConcurrentHashMap<String, String> conmap = new ConcurrentHashMap<String, String>();
conmap.put("key1", "value1");
conmap.put("key2", "value2");
conmap.put("key3", "value3");
conmap.put("key4", "value4");
conmap.put("key5", "value5");
Iterator<String> it1=conmap.keySet().iterator();
while(it1.hasNext()){
String key=it1.next();
if(key.equals("key2")){
conmap.put("key6", "value6");
}
}
System.out.println(conmap);
}
}
从上面的测试代码运行结果来看,可以得出以下几个结论:
(1)HashMap可以put进去key或value为null的键值对(可以同时为null)
(2)ConcurrentHashMap是不可以put进去key或者value为null的键值对
(3)对于HashMap的key迭代器,在迭代同时进行数据修改的话,会出现异常java.util.ConcurrentModificationException
(4)对于ConcurrentHashMap在迭代器内进行数据修改,是不会出现上面的错误
其中对于上面的(2)可以通过ConcurrentHashMap的源码可以看出来,ConcurrentHashMap不允许put进去的键值对的键或者值为null,否则就会报错
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
*
*The value can be retrieved by calling the {@code get} method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
测试代码2:
package ConcurrentTest;
import java.util.Iterator;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class ConcurrentDataModify {
// static boolean flag=false;
// static Timer timer;
public static ConcurrentHashMap<String, CopyOnWriteArrayList<String>> map = new ConcurrentHashMap<String, CopyOnWriteArrayList<String>>();
public static void main(String[] args) {
int i=5;
while(i-->0){
System.out.println("第"+i+"次运行!");
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("k"+String.valueOf(i)+"_"+String.valueOf(i+1));
list.add("k"+String.valueOf(i)+"_"+String.valueOf(i+2));
list.add("k"+String.valueOf(i)+"_"+String.valueOf(i+3));
map.put("k"+String.valueOf(i), list);
}
System.out.println(map);
ConcurrentDataModify m=new ConcurrentDataModify();
Timer timer1=new Timer();
TimerTask1 task1=m.new TimerTask1();
task1.timer=timer1;
timer1.schedule(task1, 0,1000);
// if(flag){
// timer1.cancel();
// }
Timer timer2=new Timer();
timer2.schedule(m.new TimerTask1(), 0,200);
}
class TimerTask1 extends TimerTask{
Timer timer;
@Override
public void run() {
int random1 = new Random().nextInt(5);
int random2 = new Random().nextInt(5);
int random3 = new Random().nextInt(5);
if(map.size()==0){
map.put("k"+String.valueOf(random3), new CopyOnWriteArrayList<String>());
}else{
map.remove("k"+String.valueOf(random1));
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list=map.get("k"+String.valueOf(random2));
// if(list==null){
// System.out.println("list为空!");
// timer.cancel();
this.cancel();
// return;
// }
if(list!=null){
if(list.size()==0){
list.add("k"+String.valueOf(random3)+"_"+String.valueOf(random3+1));
}else{
System.out.println(list.size());
Iterator<String> it=list.iterator();
int i=0;
while(it.hasNext()){
String cur=it.next();
cur+="_modified";
list.set(i, cur);
i++;
}
}
//ConcurrentHashMap的put和replace方法区别?
map.put("k"+String.valueOf(random2), list);
System.out.println("-------------");
System.out.println(map);
// map.replace("k"+String.valueOf(random2), list);
}
}
}
}
}
上面的代码模拟了并发修改ConcurrentHashMap和CopyOnWriteArrayList的数据,可以看出来,多线程并发修改数据的情况下,ConcurrentHashMap和CopyOnWriteArrayList是可以支持并发修改的。
可能同时修改某一数据情况也是可以支持的,至于其内部实现,分享一篇很详细的博文:
Java并发编程:并发容器之ConcurrentHashMap(转载)
通过上面的测试,得到以下几个结论
(1)如何立刻取消当前Timer和TimerTask,并不执行后面的代码(当满足某个条件的时候)?
需要在run()方法内部满足该条件时,退出run方法,同时为了使定时任务之后进来的任务也不执行,则需要调用timer的cancel()方法,实现之后任务的终止。
这里需要注意的是Timer和TimerTask这两个类中的cancel()方法的区别。
下面贴出源码:
Timer的cancel()方法
/**
* Terminates this timer, discarding any currently scheduled tasks.
* Does not interfere with a currently executing task (if it exists).
* Once a timer has been terminated, its execution thread terminates
* gracefully, and no more tasks may be scheduled on it.
*
* <p>Note that calling this method from within the run method of a
* timer task that was invoked by this timer absolutely guarantees that
* the ongoing task execution is the last task execution that will ever
* be performed by this timer.
*
* <p>This method may be called repeatedly; the second and subsequent
* calls have no effect.
*/
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); // In case queue was already empty.
}
}
从上面的注释可以看出来,该方法会舍弃正在排队等候的任务,但是并不会终止正在执行的任务。这也可以通过笔者上面的测试代码进行测试来验证。
这段代码,注释掉return,只保留timer.cancel()之后,会发现,程序仍然在执行,这句代码之后的代码。执行完这一遍之后就不会继续执行之后的任务(因为之后的任务已经被终止了)。
TimerTask的cancel()方法源码:
/**
* Cancels this timer task. If the task has been scheduled for one-time
* execution and has not yet run, or has not yet been scheduled, it will
* never run. If the task has been scheduled for repeated execution, it
* will never run again. (If the task is running when this call occurs,
* the task will run to completion, but will never run again.)
*
* <p>Note that calling this method from within the <tt>run</tt> method of
* a repeating timer task absolutely guarantees that the timer task will
* not run again.
*
* <p>This method may be called repeatedly; the second and subsequent
* calls have no effect.
*
* @return true if this task is scheduled for one-time execution and has
* not yet run, or this task is scheduled for repeated execution.
* Returns false if the task was scheduled for one-time execution
* and has already run, or if the task was never scheduled, or if
* the task was already cancelled. (Loosely speaking, this method
* returns <tt>true</tt> if it prevents one or more scheduled
* executions from taking place.)
*/
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
从上面的代码来看,TimerTask类的cancel()方法并不会取消正在执行的任务,它会等他执行完毕。
因此如果要立刻停止任务,不执行后面的代码,而且之后也不会执行任务,就必须在run()方法内部调用Timer的cancel()方法,同时return。
(2)如果要用flag标记来完成上面说的终止任务的目标(按照我上面的测试代码(已经注释掉)来做的话),是不可以的。至于为什么,读者可以自己测试思考一下。
(3)对于ConcurrentHashMap的put和replace方法区别源码
replace()方法,会返回之前key对应的值
/**
* {@inheritDoc}
*
* @return the previous value associated with the specified key,
* or {@code null} if there was no mapping for the key
* @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
if (key == null || value == null)
throw new NullPointerException();
return replaceNode(key, value, null);
}
put方法,要求key和value都不能为null,也会返回之前key对应的value
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
*
* <p>The value can be retrieved by calling the {@code get} method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
“`