本篇博文主要是初步试用并测试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)
// 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 {
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);
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){
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++;
}
}
map.put(
"k"+String.valueOf(random2),
list);
System.out.println(
"-------------");
System.out.println(
map);
}
}
}
}
}
上面的代码模拟了并发修改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);
}
“`