Java 线程崩溃
我有一个简单的项目,我正在努力更好地理解Java中的锁定和线程。实际上,我有一个缓存对象,它启动一个清理线程来删除超过给定时间的项。测试类“Tester”运行另外两个线程,一个用于向缓存添加项目,另一个用于打印缓存的内容。出于某种原因,当清理线程修改缓存中嵌入的HashMap时,它会停止任何进一步的迭代。我尝试了同步访问器/变异器方法,以及围绕缓存中的锁对象进行同步。任何想法或帮助都将由梅贝诺提供Java 线程崩溃,java,multithreading,Java,Multithreading,我有一个简单的项目,我正在努力更好地理解Java中的锁定和线程。实际上,我有一个缓存对象,它启动一个清理线程来删除超过给定时间的项。测试类“Tester”运行另外两个线程,一个用于向缓存添加项目,另一个用于打印缓存的内容。出于某种原因,当清理线程修改缓存中嵌入的HashMap时,它会停止任何进一步的迭代。我尝试了同步访问器/变异器方法,以及围绕缓存中的锁对象进行同步。任何想法或帮助都将由梅贝诺提供 公共类缓存 { private HashMap cachedObjects=new HashMap
公共类缓存
{
private HashMap cachedObjects=new HashMap();
私有静态缓存=null;
私有静态int TIME_TO_KEEP=60000;//60秒
私有最终静态对象锁=新对象();
公共静态缓存getInstance()
{
if(缓存==null)
{
缓存=新缓存();
}
返回缓存;
}
专用缓存()
{
ScheduledExecutorService executor=Executors.newScheduledThreadPool(2);
可运行任务=()->{
已同步(锁定)
{
System.out.println(“清理”);
Set keys=cachedObjects.keySet();
final long now=System.currentTimeMillis();
密钥。forEach(k->{
尝试
{
{
ObjectWrapper ow=cachedObjects.get(k);
if(ow.getExpireTime(){
System.out.println(“添加id:+++id”);
对象o=新对象();
cache.addObject(id,o);
};
带有固定延迟的执行器调度(加法器,5,10,时间单位秒);
可运行测试仪=()->{
long currTime=System.currentTimeMillis();
System.out.println(“测试:”);
Set keys=cache.getKeys();
密钥。forEach(k->{
ObjectWrapper o=cache.getObj(k);
System.out.println(k+“>”+currTime+:“+o.getExpireTime());
});
};
执行者。固定延迟的时间表(测试仪,20,10,时间单位。秒);
}
}
公共类对象包装器
{
私有对象对象;
私有长过期时间;
公共对象包装器(对象对象,长过期时间)
{
this.obj=obj;
this.expireTime=expireTime;
}
公共对象getObj()
{
返回obj;
}
公共无效设置对象(对象对象对象)
{
this.obj=obj;
}
公共长getExpireTime()
{
返回到期时间;
}
public void setExpireTime(长expireTime)
{
this.expireTime=expireTime;
}
}
考虑使用ConcurrentHashMap
,这是一种本机线程安全的映射实现,而不是HashMap
你的错误主要是:
public Collection<ObjectWrapper> getValues()
{
synchronized(LOCK)
{
return cachedObjects.values();
}
}
public Set<Object> getKeys()
{
synchronized(LOCK)
{
return cachedObjects.keySet();
}
}
您还需要使ObjectWrapper
也成为线程安全的,因为它意味着要共享,否则您的代码将不会仍然是线程安全的。最简单的方法是使其不变,如下所示:
public class ObjectWrapper
{
private final Object obj;
private final long expireTime;
public ObjectWrapper(Object obj, long expireTime)
{
this.obj = obj;
this.expireTime = expireTime;
}
public Object getObj()
{
return obj;
}
public long getExpireTime()
{
return expireTime;
}
}
在对键集进行迭代时,您正在从键集中删除元素,从而产生一个
ConcurrentModificationException
,该异常在下一次迭代时抛出(在try-catch块之外)。如果将try-catch块移到循环外部:
private Cache()
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Runnable task = () -> {
synchronized(LOCK)
{
try
{
System.out.println("Cleanup");
Set<Object> keys = cachedObjects.keySet();
final long now = System.currentTimeMillis();
keys.forEach(k -> {
{
ObjectWrapper ow = cachedObjects.get(k);
if(ow.getExpireTime() < now)
{
int size = cachedObjects.size();
cachedObjects.remove(k, ow);
System.out.println("DEL:" + k + ", from " + size + " to " + cachedObjects.size());
}
}
});
}
catch (Throwable t)
{
t.printStackTrace();
}
}
};
executor.scheduleWithFixedDelay(task, 5, 15, TimeUnit.SECONDS);
}
相反,在键集上使用迭代器:
private Cache()
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Runnable task = () -> {
synchronized(LOCK)
{
try
{
System.out.println("Cleanup");
final long now = System.currentTimeMillis();
final Iterator<Map.Entry<Object, ObjectWrapper>> iter = cachedObjects.entrySet().iterator();
while (iter.hasNext()) {
final Map.Entry<Object, ObjectWrapper> entry = iter.next();
final ObjectWrapper ow = entry.getValue();
if(ow.getExpireTime() < now)
{
final Object k = entry.getKey();
int size = cachedObjects.size();
iter.remove();
System.out.println("DEL:" + k + ", from " + size + " to " + cachedObjects.size());
}
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
};
executor.scheduleWithFixedDelay(task, 5, 15, TimeUnit.SECONDS);
}
谢谢,我可以通过使用ConcurrentHashMap而不是HashMap使它工作。我不确定如何使ObjectWrapper不可变,因为它只是一个嵌套对象,并且直接被其他线程更改。我确信这只是我的无知。在多线程环境中,规则是始终共享线程安全对象,因此在这里,当您共享ObjectWrapper类型的实例时,该类必须是线程安全的。我不认为在键上获取迭代器,这完全有道理,因为它将是一个新对象,从影响原始集合的任何可能更改中删除。
private Cache()
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Runnable task = () -> {
synchronized(LOCK)
{
try
{
System.out.println("Cleanup");
Set<Object> keys = cachedObjects.keySet();
final long now = System.currentTimeMillis();
keys.forEach(k -> {
{
ObjectWrapper ow = cachedObjects.get(k);
if(ow.getExpireTime() < now)
{
int size = cachedObjects.size();
cachedObjects.remove(k, ow);
System.out.println("DEL:" + k + ", from " + size + " to " + cachedObjects.size());
}
}
});
}
catch (Throwable t)
{
t.printStackTrace();
}
}
};
executor.scheduleWithFixedDelay(task, 5, 15, TimeUnit.SECONDS);
}
java.util.ConcurrentModificationException
at java.util.HashMap$KeySet.forEach(HashMap.java:935)
at Cache.lambda$new$1(Cache.java:32)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
private Cache()
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
Runnable task = () -> {
synchronized(LOCK)
{
try
{
System.out.println("Cleanup");
final long now = System.currentTimeMillis();
final Iterator<Map.Entry<Object, ObjectWrapper>> iter = cachedObjects.entrySet().iterator();
while (iter.hasNext()) {
final Map.Entry<Object, ObjectWrapper> entry = iter.next();
final ObjectWrapper ow = entry.getValue();
if(ow.getExpireTime() < now)
{
final Object k = entry.getKey();
int size = cachedObjects.size();
iter.remove();
System.out.println("DEL:" + k + ", from " + size + " to " + cachedObjects.size());
}
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
};
executor.scheduleWithFixedDelay(task, 5, 15, TimeUnit.SECONDS);
}
Cleanup
Adding id:1
Adding id:2
Cleanup
Test:
1>1467228864763::1467228909763
2>1467228864763::1467228919764
Adding id:3
Test:
1>1467228874766::1467228909763
2>1467228874766::1467228919764
3>1467228874766::1467228929764
Cleanup
Adding id:4
Test:
1>1467228884766::1467228909763
2>1467228884766::1467228919764
3>1467228884766::1467228929764
4>1467228884766::1467228939764
Adding id:5
Cleanup
Test:
1>1467228894770::1467228909763
2>1467228894770::1467228919764
3>1467228894770::1467228929764
4>1467228894770::1467228939764
5>1467228894770::1467228949765
Adding id:6
Test:
1>1467228904771::1467228909763
2>1467228904771::1467228919764
3>1467228904771::1467228929764
4>1467228904771::1467228939764
5>1467228904771::1467228949765
6>1467228904771::1467228959765
Cleanup
Adding id:7
Test:
1>1467228914771::1467228909763
2>1467228914771::1467228919764
3>1467228914771::1467228929764
4>1467228914771::1467228939764
5>1467228914771::1467228949765
6>1467228914771::1467228959765
7>1467228914771::1467228969765
Adding id:8
Cleanup
DEL:1, from 8 to 7
DEL:2, from 7 to 6
Test:
3>1467228924772::1467228929764
4>1467228924772::1467228939764
5>1467228924772::1467228949765
6>1467228924772::1467228959765
7>1467228924772::1467228969765
8>1467228924772::1467228979765
Adding id:9
Test:
3>1467228934772::1467228929764
4>1467228934772::1467228939764
5>1467228934772::1467228949765
6>1467228934772::1467228959765
7>1467228934772::1467228969765
8>1467228934772::1467228979765
9>1467228934772::1467228989766
Cleanup
DEL:3, from 7 to 6
Adding id:10
Test:
4>1467228944773::1467228939764
5>1467228944773::1467228949765
6>1467228944773::1467228959765
7>1467228944773::1467228969765
8>1467228944773::1467228979765
9>1467228944773::1467228989766
10>1467228944773::1467228999766
Adding id:11
Cleanup
DEL:4, from 8 to 7
DEL:5, from 7 to 6
Test:
6>1467228954773::1467228959765
7>1467228954773::1467228969765
8>1467228954773::1467228979765
9>1467228954773::1467228989766
10>1467228954773::1467228999766
11>1467228954773::1467229009766
Adding id:12
Test:
6>1467228964774::1467228959765
7>1467228964774::1467228969765
8>1467228964774::1467228979765
9>1467228964774::1467228989766
10>1467228964774::1467228999766
11>1467228964774::1467229009766
12>1467228964774::1467229019767
Cleanup
DEL:6, from 7 to 6
Adding id:13