java:在多线程中从 TreeMap 访问值时出现问题
java: issue while access value from TreeMap in multithreading
我已经使用 TreeMap 来存储键值。
对于使用自定义对象的键。
但是一旦遇到非常奇怪的问题,我就无法获得我之前设置的值(使用相同的密钥)。
下面是我的代码
public final class TestOptions implements Cloneable {
private Map<StorageSystemOptionKey, Object> options = new TreeMap<StorageSystemOptionKey, Object>();
private static final class StorageSystemOptionKey implements Comparable<StorageSystemOptionKey> {
/** Constant used to create hashcode */
private static final int HASH = 31;
private final Class<? extends StorageRepository> storageRepositoryClass;
/** The option name */
private final String name;
private StorageSystemOptionKey(Class<? extends StorageRepository> storageRepositoryClass, String name) {
this.storageRepositoryClass = storageRepositoryClass;
this.name = name;
}
public int compareTo(StorageSystemOptionKey o) {
int ret = storageRepositoryClass.getName().compareTo(o.storageRepositoryClass.getName());
if (ret != 0) {
return ret;
}
return name.compareTo(o.name);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final StorageSystemOptionKey that = (StorageSystemOptionKey) o;
if (!storageRepositoryClass.equals(that.storageRepositoryClass)) {
return false;
}
if (!name.equals(that.name)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result;
result = storageRepositoryClass.hashCode();
result = HASH * result + name.hashCode();
return result;
}
}
void setOption(Class<? extends StorageRepository> fileSystemClass, String name, Object value) {
options.put(new StorageSystemOptionKey(fileSystemClass, name), value);
}
Object getOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.get(key);
}
boolean hasOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.containsKey(key);
}
public int compareTo(TestOptions other) {
if (this == other) {
return 0;
}
int propsSz = options == null ? 0 : options.size();
int propsFkSz = other.options == null ? 0 : other.options.size();
if (propsSz < propsFkSz) {
return -1;
}
if (propsSz > propsFkSz) {
return 1;
}
if (propsSz == 0) {
return 0;
}
int hash = options.hashCode();
int hashFk = other.options.hashCode();
if (hash < hashFk) {
return -1;
}
if (hash > hashFk) {
return 1;
}
return 0;
}
@Override
public Object clone() {
TestOptions clone = new TestOptions();
clone.options = new TreeMap<StorageSystemOptionKey, Object>(options);
return clone;
}
}
调用方法设置和获取点赞
public abstract Class<? extends StorageRepository> getStorageRepositoryClass();
public Class<? extends StorageRepository> getStorageRepositoryClass() {
return MyImpl.class;
}
TestOptions opt =new TestOptions(); // shared accross all Threads
Object getProperty(String name) {
return opt.getOption(getStorageRepositoryClass(), name);
}
void setProperty(String name, Object value) {
opt.setOption(getStorageRepositoryClass(), name, value);
}
在多线程应用程序中使用set 和get 方法。
查询:
我多次调用 set/get 然后我也无法获得之前设置的值(相同的键)
这是因为 Treeset 实现不同步
或者 hashCode、equals 或 compareTo 方法实现有问题?
TreeSet 未同步。我相信 ConcurrentSkipListMap 可能会更好。
快速浏览一下,您的 compareTo()
、equals()
和 hashCode()
看起来不错。请注意,TreeMap
将主要使用 compareTo()
来查找元素,因此该方法需要正确(您的方法在技术上看起来是正确的)。
但是,TreeMap
和 TreeSet
(以及其他基本集合和映射)不是线程安全的,因此并发修改可能会导致各种意外行为。我们曾经遇到过这样的情况,其中 2 个线程试图将单个元素添加到 hashmap,并且线程最终陷入无限循环,因为解决冲突的内部列表产生了一个循环(由于并发 put)。
因此要么使用 ConcurrentXxxx
地图和集合,要么同步访问您的地图和集合。
我已经使用 TreeMap 来存储键值。 对于使用自定义对象的键。 但是一旦遇到非常奇怪的问题,我就无法获得我之前设置的值(使用相同的密钥)。 下面是我的代码
public final class TestOptions implements Cloneable {
private Map<StorageSystemOptionKey, Object> options = new TreeMap<StorageSystemOptionKey, Object>();
private static final class StorageSystemOptionKey implements Comparable<StorageSystemOptionKey> {
/** Constant used to create hashcode */
private static final int HASH = 31;
private final Class<? extends StorageRepository> storageRepositoryClass;
/** The option name */
private final String name;
private StorageSystemOptionKey(Class<? extends StorageRepository> storageRepositoryClass, String name) {
this.storageRepositoryClass = storageRepositoryClass;
this.name = name;
}
public int compareTo(StorageSystemOptionKey o) {
int ret = storageRepositoryClass.getName().compareTo(o.storageRepositoryClass.getName());
if (ret != 0) {
return ret;
}
return name.compareTo(o.name);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final StorageSystemOptionKey that = (StorageSystemOptionKey) o;
if (!storageRepositoryClass.equals(that.storageRepositoryClass)) {
return false;
}
if (!name.equals(that.name)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result;
result = storageRepositoryClass.hashCode();
result = HASH * result + name.hashCode();
return result;
}
}
void setOption(Class<? extends StorageRepository> fileSystemClass, String name, Object value) {
options.put(new StorageSystemOptionKey(fileSystemClass, name), value);
}
Object getOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.get(key);
}
boolean hasOption(Class<? extends StorageRepository> fileSystemClass, String name) {
StorageSystemOptionKey key = new StorageSystemOptionKey(fileSystemClass, name);
return options.containsKey(key);
}
public int compareTo(TestOptions other) {
if (this == other) {
return 0;
}
int propsSz = options == null ? 0 : options.size();
int propsFkSz = other.options == null ? 0 : other.options.size();
if (propsSz < propsFkSz) {
return -1;
}
if (propsSz > propsFkSz) {
return 1;
}
if (propsSz == 0) {
return 0;
}
int hash = options.hashCode();
int hashFk = other.options.hashCode();
if (hash < hashFk) {
return -1;
}
if (hash > hashFk) {
return 1;
}
return 0;
}
@Override
public Object clone() {
TestOptions clone = new TestOptions();
clone.options = new TreeMap<StorageSystemOptionKey, Object>(options);
return clone;
}
}
调用方法设置和获取点赞
public abstract Class<? extends StorageRepository> getStorageRepositoryClass();
public Class<? extends StorageRepository> getStorageRepositoryClass() {
return MyImpl.class;
}
TestOptions opt =new TestOptions(); // shared accross all Threads
Object getProperty(String name) {
return opt.getOption(getStorageRepositoryClass(), name);
}
void setProperty(String name, Object value) {
opt.setOption(getStorageRepositoryClass(), name, value);
}
在多线程应用程序中使用set 和get 方法。
查询:
我多次调用 set/get 然后我也无法获得之前设置的值(相同的键)
这是因为 Treeset 实现不同步
或者 hashCode、equals 或 compareTo 方法实现有问题?
TreeSet 未同步。我相信 ConcurrentSkipListMap 可能会更好。
快速浏览一下,您的 compareTo()
、equals()
和 hashCode()
看起来不错。请注意,TreeMap
将主要使用 compareTo()
来查找元素,因此该方法需要正确(您的方法在技术上看起来是正确的)。
但是,TreeMap
和 TreeSet
(以及其他基本集合和映射)不是线程安全的,因此并发修改可能会导致各种意外行为。我们曾经遇到过这样的情况,其中 2 个线程试图将单个元素添加到 hashmap,并且线程最终陷入无限循环,因为解决冲突的内部列表产生了一个循环(由于并发 put)。
因此要么使用 ConcurrentXxxx
地图和集合,要么同步访问您的地图和集合。