Skip to content

Commit 604c29e

Browse files
committed
mutex, factory & sync - all of the tests are green.
0 parents  commit 604c29e

File tree

8 files changed

+502
-0
lines changed

8 files changed

+502
-0
lines changed

pom.xml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.antkorwin</groupId>
7+
<artifactId>xsync</artifactId>
8+
<version>0.0.1-SNAPSHOT</version>
9+
<packaging>jar</packaging>
10+
11+
<name>xsync</name>
12+
<description>Custom synchronization tools</description>
13+
14+
<parent>
15+
<groupId>org.springframework.boot</groupId>
16+
<artifactId>spring-boot-starter-parent</artifactId>
17+
<version>2.0.2.RELEASE</version>
18+
<relativePath/> <!-- lookup parent from repository -->
19+
</parent>
20+
21+
<properties>
22+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
24+
<java.version>1.8</java.version>
25+
</properties>
26+
27+
<dependencies>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter</artifactId>
31+
</dependency>
32+
33+
<dependency>
34+
<groupId>org.projectlombok</groupId>
35+
<artifactId>lombok</artifactId>
36+
<optional>true</optional>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.springframework.boot</groupId>
40+
<artifactId>spring-boot-starter-test</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.awaitility</groupId>
45+
<artifactId>awaitility</artifactId>
46+
<version>3.1.0</version>
47+
<scope>test</scope>
48+
</dependency>
49+
</dependencies>
50+
51+
<build>
52+
<plugins>
53+
<plugin>
54+
<groupId>org.springframework.boot</groupId>
55+
<artifactId>spring-boot-maven-plugin</artifactId>
56+
</plugin>
57+
</plugins>
58+
</build>
59+
60+
61+
</project>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.antkorwin.xsync;
2+
3+
import lombok.EqualsAndHashCode;
4+
import lombok.Getter;
5+
6+
import java.util.Objects;
7+
8+
/**
9+
* Created by Korovin Anatolii on 14.06.2018.
10+
*
11+
* @author Korovin Anatolii
12+
* @version 1.0
13+
*/
14+
@EqualsAndHashCode
15+
@Getter
16+
public class XMutex<KeyT> {
17+
18+
private KeyT key;
19+
20+
public XMutex(KeyT key) {
21+
this.key = key;
22+
}
23+
24+
public static <KeyT> XMutex<KeyT> of(KeyT key) {
25+
return new XMutex<>(key);
26+
}
27+
28+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.antkorwin.xsync;
2+
3+
import java.lang.ref.WeakReference;
4+
import java.util.Collections;
5+
import java.util.Map;
6+
import java.util.Optional;
7+
import java.util.WeakHashMap;
8+
9+
/**
10+
* Created by Korovin Anatolii on 14.06.2018.
11+
*
12+
* @author Korovin Anatolii
13+
* @version 1.0
14+
*/
15+
public class XMutexFactory<KeyT> {
16+
17+
public final Map<XMutex<KeyT>, WeakReference<XMutex<KeyT>>> weakHashMap =
18+
Collections.synchronizedMap(new WeakHashMap<XMutex<KeyT>, WeakReference<XMutex<KeyT>>>());
19+
20+
21+
public XMutex<KeyT> getMutex(KeyT key) {
22+
synchronized (weakHashMap) {
23+
validateKey(key);
24+
return getExist(key)
25+
.orElseGet(() -> saveNewReference(key));
26+
}
27+
}
28+
29+
private void validateKey(KeyT key) {
30+
if (key == null) {
31+
throw new IllegalArgumentException("The KEY of mutex must not be null.");
32+
}
33+
}
34+
35+
private Optional<XMutex<KeyT>> getExist(KeyT key) {
36+
return Optional.ofNullable(weakHashMap.get(XMutex.of(key)))
37+
.map(WeakReference::get);
38+
}
39+
40+
private XMutex<KeyT> saveNewReference(KeyT key) {
41+
42+
XMutex<KeyT> mutex = XMutex.of(key);
43+
44+
WeakReference<XMutex<KeyT>> res = weakHashMap.put(mutex, new WeakReference<>(mutex));
45+
if (res != null && res.get() != null) {
46+
return res.get();
47+
}
48+
return mutex;
49+
}
50+
51+
public long size() {
52+
return weakHashMap.size();
53+
}
54+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.antkorwin.xsync;
2+
3+
/**
4+
* Created by Korovin Anatolii on 18.06.2018.
5+
*
6+
* @author Korovin Anatolii
7+
* @version 1.0
8+
*/
9+
public class XSync<KeyT> {
10+
11+
private XMutexFactory<KeyT> mutexFactory = new XMutexFactory<>();
12+
13+
public void execute(KeyT mutexKey, Runnable runnable) {
14+
XMutex<KeyT> mutex = mutexFactory.getMutex(mutexKey);
15+
synchronized (mutex) {
16+
runnable.run();
17+
}
18+
}
19+
}

src/main/resources/application.properties

Whitespace-only changes.
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package com.antkorwin.xsync;
2+
3+
import org.assertj.core.api.Assertions;
4+
import org.hamcrest.Matchers;
5+
import org.junit.Test;
6+
7+
import java.util.Collections;
8+
import java.util.List;
9+
import java.util.Set;
10+
import java.util.UUID;
11+
import java.util.concurrent.ConcurrentHashMap;
12+
import java.util.concurrent.TimeUnit;
13+
import java.util.stream.Collectors;
14+
import java.util.stream.IntStream;
15+
16+
import static org.awaitility.Awaitility.await;
17+
18+
/**
19+
* Created by Korovin Anatolii on 17.06.2018.
20+
*
21+
* @author Korovin Anatolii
22+
* @version 1.0
23+
*/
24+
public class XMutexFactoryTest {
25+
26+
27+
@Test
28+
public void testFirst() {
29+
// Arrange
30+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
31+
UUID firstId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
32+
UUID secondId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
33+
// Check precondition
34+
Assertions.assertThat(firstId != secondId).isTrue();
35+
Assertions.assertThat(firstId).isEqualTo(secondId);
36+
37+
// Act
38+
XMutex<UUID> firstMutex = mutexFactory.getMutex(firstId);
39+
XMutex<UUID> secondMutex = mutexFactory.getMutex(secondId);
40+
41+
// Asserts
42+
Assertions.assertThat(firstMutex).isNotNull();
43+
Assertions.assertThat(secondMutex).isNotNull();
44+
Assertions.assertThat(firstMutex).isEqualTo(secondMutex);
45+
Assertions.assertThat(firstMutex == secondMutex).isTrue();
46+
}
47+
48+
@Test
49+
public void testSize() {
50+
// Arrange
51+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
52+
UUID firstId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
53+
UUID secondId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
54+
UUID thirdId = UUID.randomUUID();
55+
56+
// Act
57+
XMutex<UUID> firstMutex = mutexFactory.getMutex(firstId);
58+
XMutex<UUID> secondMutex = mutexFactory.getMutex(secondId);
59+
XMutex<UUID> thirdMutex = mutexFactory.getMutex(thirdId);
60+
61+
// Asserts
62+
Assertions.assertThat(mutexFactory.size()).isEqualTo(2);
63+
Assertions.assertThat(firstMutex).isEqualTo(secondMutex);
64+
Assertions.assertThat(firstMutex).isNotEqualTo(thirdMutex);
65+
}
66+
67+
@Test
68+
public void testWithGC() throws InterruptedException {
69+
// Arrange
70+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
71+
UUID firstId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
72+
UUID secondId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
73+
74+
// Act
75+
XMutex<UUID> firstMutex = mutexFactory.getMutex(firstId);
76+
int fisrtHashCode = System.identityHashCode(firstMutex);
77+
firstMutex = null;
78+
System.gc();
79+
await().atMost(5, TimeUnit.SECONDS)
80+
.until(mutexFactory::size, Matchers.equalTo(0L));
81+
82+
XMutex<UUID> secondMutex = mutexFactory.getMutex(secondId);
83+
int secondHashCode = System.identityHashCode(secondMutex);
84+
85+
// Asserts
86+
System.out.println(fisrtHashCode + " " + secondHashCode);
87+
Assertions.assertThat(mutexFactory.size()).isEqualTo(1L);
88+
Assertions.assertThat(fisrtHashCode).isNotEqualTo(secondHashCode);
89+
}
90+
91+
@Test
92+
public void testHashCode() {
93+
// Arrange
94+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
95+
UUID firstId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
96+
UUID secondId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
97+
UUID thirdId = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
98+
99+
// Act
100+
XMutex<UUID> firstMutex = mutexFactory.getMutex(firstId);
101+
XMutex<UUID> secondMutex = mutexFactory.getMutex(secondId);
102+
XMutex<UUID> thirdMutex = mutexFactory.getMutex(thirdId);
103+
104+
// Assert
105+
Assertions.assertThat(System.identityHashCode(firstMutex))
106+
.isEqualTo(System.identityHashCode(secondMutex));
107+
108+
Assertions.assertThat(System.identityHashCode(firstMutex))
109+
.isEqualTo(System.identityHashCode(thirdMutex));
110+
}
111+
112+
113+
@Test
114+
public void testALotOfHashCodes() {
115+
// Arrange
116+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
117+
UUID id = UUID.fromString("c117c526-606e-41b6-8197-1a6ba779f69b");
118+
119+
// Act
120+
Set<Integer> hashs = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
121+
for (int i = 0; i < 1000; i++) {
122+
XMutex<UUID> mutex = mutexFactory.getMutex(id);
123+
hashs.add(System.identityHashCode(mutex));
124+
}
125+
126+
Assertions.assertThat(hashs.size()).isEqualTo(1);
127+
}
128+
129+
@Test(timeout = 11000)
130+
public void testConcurrency() throws InterruptedException {
131+
// Arrange
132+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
133+
134+
int endExclusive = 1000;
135+
136+
List<UUID> ids = IntStream.range(0, endExclusive)
137+
.boxed()
138+
.map(i -> UUID.randomUUID())
139+
.collect(Collectors.toList());
140+
System.out.println("idsSize = " + ids.size());
141+
142+
Set<XMutex<UUID>> results = Collections
143+
.newSetFromMap(new ConcurrentHashMap<XMutex<UUID>, Boolean>());
144+
145+
// Act
146+
// ExecutorService executorService = Executors.newFixedThreadPool(10);
147+
// IntStream.range(0, 10000)
148+
// .boxed()
149+
// .forEach(i -> {
150+
// executorService.submit(() -> {
151+
// UUID uuid = ids.get(i % endExclusive);
152+
// XMutex<UUID> mutex = mutexFactory.getMutex(uuid);
153+
// Assertions.assertThat(mutex).isNotNull();
154+
// results.add(mutex);
155+
// });
156+
// });
157+
158+
IntStream.range(0, 10000)
159+
.boxed()
160+
.parallel()
161+
.forEach(i -> {
162+
UUID uuid = ids.get(i % endExclusive);
163+
XMutex<UUID> mutex = mutexFactory.getMutex(uuid);
164+
Assertions.assertThat(mutex).isNotNull();
165+
results.add(mutex);
166+
});
167+
168+
await().atMost(10, TimeUnit.SECONDS)
169+
.until(results::size, Matchers.equalTo(endExclusive));
170+
171+
System.out.println("res= " + results.size());
172+
System.out.println("mf= " + mutexFactory.size());
173+
174+
// Asserts
175+
Assertions.assertThat(results).hasSize(endExclusive);
176+
Assertions.assertThat(mutexFactory.size()).isEqualTo(endExclusive);
177+
}
178+
179+
@Test
180+
public void testWithNullKey() {
181+
// Arrange
182+
XMutexFactory<UUID> mutexFactory = new XMutexFactory<>();
183+
// Act
184+
try {
185+
mutexFactory.getMutex(null);
186+
} catch (Exception e) {
187+
// Asserts
188+
Assertions.assertThat(e)
189+
.isInstanceOf(IllegalArgumentException.class)
190+
.hasMessage("The KEY of mutex must not be null.");
191+
}
192+
}
193+
}

0 commit comments

Comments
 (0)