这是解释如何使用Hazelcast的系列文章的续篇。 如果一个人没有阅读其他六个帖子,请转到目录并阅读其他帖子。
不同的地图种类
Hazelcast的MultiMap打破了以前使用java.util.Collection接口的常规方式。 实际上,在我看来,MultiMap的概念完全打破了地图的概念。 当法线贴图将一个键关联到一个值时,MultiMap可以将多个值映射到同一键 。 这是一个非常重要的概念,同一键有多个值。 值可以存储在两个不同的集合(集合或列表)中。 这些集合的行为类似于java.util.Collections库的集合。
安全吗?
MultiMap有一种疯狂的方法。 在法线映射中,每个键可以存储多个值,但必须手动完成。 这意味着将集合从存储中取出,进行任何更改,然后将集合放回存储中。 这对于线程安全可能是有问题的,因为先前的步骤需要原子完成,或者存在其他线程读取陈旧或不一致的数据的可能性。 MultiMaps通过提供以下服务来帮助解决此问题:
- 可以通过一次put操作添加一个值。
- 一个人可以用钥匙锁定一个条目。 这是关键(双关语),因为这意味着开发人员不必跟踪每个条目的单独锁。
例
这个示例有些不同,因为在运行示例时,我使用Maven的failsafe插件作为主要引擎。 是的,我写了两个示例,因为我想展示使用MultiMap的两种不同方式。 一种方法是,每个线程都拥有自己的游乐场,被分配一个唯一的密钥,或被分配一个共享的游乐场,或者所有线程共享相同的密钥。 这也是如何将Hazelcast的IdGenerator用作在应用程序中创建线程安全性的方法的示例。
Pom文件
请记住,此示例代码利用了Apache的Failsafe Maven插件 。 故障安全插件通过在第一次失败时不终止构建来帮助进行自动集成测试。 它是surefire插件的分支。 我也一直在尝试使用Maven提供的报告。 在命令行中输入“ mvn site”,它将生成一个网站。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0>
<groupId>com.darylmathison>
<artifactId>hazelcast-multimap-example>
<version>1.0-SNAPSHOT>
<description>Examples of using Hazelcast Multimap>
<developers>
<developer>
<name>Daryl Mathison>
<id>dmathison>
<roles>
<role>developer>
>
>
>
<scm>
<connection>scm:git:https://github.com/darylmathison/hazelcast-multimap-example.git>
<url>https://github.com/darylmathison/hazelcast-multimap-example>
>
<properties>
<maven.compiler.source>1.8>
<maven.compiler.target>1.8>
<project.build.sourceEncoding>UTF-8>
>
<dependencies>
<dependency>
<groupId>com.hazelcast>
<artifactId>hazelcast>
<version>3.4.2>
>
<dependency>
<groupId>junit>
<artifactId>junit>
<version>4.12>
<scope>test>
>
>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins>
<artifactId>maven-failsafe-plugin>
<version>2.18.1>
<executions>
<execution>
<goals>
<goal>integration-test>
<goal>verify>
>
>
>
>
>
>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins>
<artifactId>maven-project-info-reports-plugin>
<version>2.7>
<reportSets>
<reportSet>
<reports>
<report>dependencies>
<report>index>
<report>project-team>
<report>scm>
>
>
>
>
<plugin>
<groupId>org.apache.maven.plugins>
<artifactId>maven-javadoc-plugin>
<version>2.10.3>
<reportSets>
<reportSet>
<reports>
<report>javadoc>
<report>test-javadoc>
>
>
>
>
<plugin>
<groupId>org.apache.maven.plugins>
<artifactId>maven-surefire-report-plugin>
<version>2.18.1>
>
<plugin>
<groupId>org.apache.maven.plugins>
<artifactId>maven-jxr-plugin>
<version>2.5>
<configuration>
<linkJavadoc>true>
>
<reportSets>
<reportSet>
<reports>
<report>jxr>
<report>test-jxr>
>
>
>
>
>
>
>
MultimapAccessThread
这是每个访问类型线程的基类。
package com.darylmathison.multimap;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceAware;
import com.hazelcast.core.MultiMap;
import java.io.Serializable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Abstract class to access MultiMap.
*/
public abstract class MultiMapAccessThread implements Serializable, Runnable, HazelcastInstanceAware {
protected com.hazelcast.core.HazelcastInstance instance;
protected MultiMap<Long, Long> map;
protected String mapName;
protected Lock l = new ReentrantLock();
public void setHazelcastInstance(HazelcastInstance instance) {
l.lock();
try {
this.instance = instance;
if (mapName != null && !mapName.isEmpty()) {
map = instance.getMultiMap(mapName);
}
} finally {
l.unlock();
}
}
public String getMapName() {
return mapName;
}
public void setMapName(String mapName) {
l.lock();
try {
this.mapName = mapName;
} finally {
l.unlock();
}
}
}
IdMultiMapAccessThread
package com.darylmathison.multimap;
/**
* This thread accesses only one "slot" in a multimap.
*/
public class IdMultiMapAccessThread extends MultiMapAccessThread {
private Long id;
@Override
public void run() {
l.lock();
boolean shouldRun = (map != null && id != null);
l.unlock();
if(shouldRun) {
for (long i = 0; i < 10; i++) {
map.put(id, i);
}
}
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
GroupMultiMapAccessThread
package com.darylmathison.multimap;
/**
* Thread designed to share the same "slot" on a MultiMap.
*/
public class GroupMultiMapAccessThread extends MultiMapAccessThread {
private static final long MAX = 10;
private Long groupId;
/**
* When an object implementing interface Runnable is used
* to create a thread, starting the thread causes the object's
* run method to be called in that separately executing
* thread.
*
* The general contract of the method run is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@Override
public void run() {
l.lock();
boolean shouldRun = (groupId != null && map != null);
l.unlock();
if(shouldRun) {
map.lock(groupId);
try {
if (map.get(groupId).isEmpty()) {
System.out.println("adding to list");
for (long i = 0; i < MAX; i++) {
map.put(groupId, i);
}
} else {
System.out.println("nothing to add");
}
} finally {
map.unlock(groupId);
}
}
}
public void setGroupId(Long groupId) {
l.lock();
this.groupId = groupId;
l.unlock();
}
}
HazelcastInstanceResource
此规则启动并关闭运行线程所需的Hazelcast实例。
package com.darylmathison.multimap.test.rule;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IExecutorService;
import org.junit.rules.ExternalResource;
/**
* Created by Daryl on 4/27/2015.
*/
public class HazelcastInstanceResource extends ExternalResource {
public static final String SERVICE_NAME = "Charlotte";
HazelcastInstance instance;
IExecutorService service;
@Override
protected void before() throws Throwable {
super.before();
instance = Hazelcast.newHazelcastInstance();
service = instance.getExecutorService(SERVICE_NAME);
}
@Override
protected void after() {
super.after();
service.shutdown();
instance.shutdown();
}
public HazelcastInstance getInstance() {
return instance;
}
public IExecutorService getService() {
return service;
}
}
IdMultiMapAccessIT
这是一个使用IdGenerator为线程放置数据的新“操场”或键的示例。
package com.darylmathison.multimap;
import com.darylmathison.multimap.test.rule.HazelcastInstanceResource;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.IdGenerator;
import org.junit.ClassRule;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* Integration test for IdMultiMapAccessThread
*/
public class IdMultiMapAccessThreadIT {
public static final String MAP_NAME = "idAccessMap";
public static final String GEN_NAME = "singleAccess";
public static final int NUM_THREADS = 10;
@ClassRule
public static HazelcastInstanceResource hazelcastInstanceResource = new HazelcastInstanceResource();
@Test
public void testIdThreads() {
List threads = generateThreads(hazelcastInstanceResource.getInstance());
List<Future<?>> futures = new ArrayList<>(NUM_THREADS);
IExecutorService spinner = hazelcastInstanceResource.getService();
for(IdMultiMapAccessThread thread: threads) {
futures.add(spinner.submit(thread));
}
for(Future<?> future: futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
private List generateThreads(HazelcastInstance instance) {
IdGenerator gen = instance.getIdGenerator(GEN_NAME);
List threads = new ArrayList<>(NUM_THREADS);
for(int i = 0; i < NUM_THREADS; i++) {
IdMultiMapAccessThread thread = new IdMultiMapAccessThread();
thread.setMapName(MAP_NAME);
thread.setId(gen.newId());
threads.add(thread);
}
return threads;
}
}
GroupMultiMapAccessThreadIT
这是使用IdGenerator创建共享游乐场或插槽的示例。
package com.darylmathison.multimap;
import com.darylmathison.multimap.test.rule.HazelcastInstanceResource;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.IdGenerator;
import org.junit.ClassRule;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* GroupMultiMapAccessThread Integration Test
*/
public class GroupMultiMapAccessThreadIT {
public static final int NUM_THREADS = 10;
public static final String GEN_NAME = "groupIdGenerator";
public static final String MAP_NAME = "groupMap";
@ClassRule
public static HazelcastInstanceResource hazelcastInstanceResource = new HazelcastInstanceResource();
@Test
public void testGroupMultiMapAccessThread() {
List threads = createThreads();
IExecutorService service = hazelcastInstanceResource.getService();
List<Future<?>> futures = new ArrayList<>(NUM_THREADS);
for(GroupMultiMapAccessThread thread: threads) {
futures.add(service.submit(thread));
}
for(Future<?> future: futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
private List createThreads() {
List ret = new ArrayList<>(NUM_THREADS);
IdGenerator gen = hazelcastInstanceResource.getInstance().getIdGenerator(GEN_NAME);
Long groupId = gen.newId();
for(int i = 0; i < NUM_THREADS; i++) {
GroupMultiMapAccessThread thread = new GroupMultiMapAccessThread();
thread.setMapName(MAP_NAME);
thread.setGroupId(groupId);
ret.add(thread);
}
return ret;
}
}
结论
在这篇文章中,介绍了Hazelcast的MultiMap。 结果表明,MultiMaps可以为给定键存储多个值。 还显示了线程如何使用IdGenerator作为可能的密钥生成器在MultiMap中共享数据或如何为其自身存储数据。 该代码可以在GitHub的此处找到。
参考文献
翻译自: https://www.javacodegeeks.com/2015/04/beginners-guide-to-hazelcast-part-7.html