依赖于方法参数的Java同步
如何根据方法参数值提供同步 应同步使用“相同”参数值A的所有方法调用。具有不同参数值(例如B)的方法调用可以访问,即使具有A的调用已经在等待。对B的下一个并发调用也必须等待释放第一个B 我的用例:我想同步ID级别上对JPA实体的访问,但希望避免悲观锁定,因为我需要某种队列。用于锁定的“键””旨在成为实体ID——实际上是Java Long类型依赖于方法参数的Java同步,java,rest,synchronization,synchronized,Java,Rest,Synchronization,Synchronized,如何根据方法参数值提供同步 应同步使用“相同”参数值A的所有方法调用。具有不同参数值(例如B)的方法调用可以访问,即使具有A的调用已经在等待。对B的下一个并发调用也必须等待释放第一个B 我的用例:我想同步ID级别上对JPA实体的访问,但希望避免悲观锁定,因为我需要某种队列。用于锁定的“键””旨在成为实体ID——实际上是Java Long类型 protected void entityLockedAccess(SomeEntity myEntity) { //getId() returns
protected void entityLockedAccess(SomeEntity myEntity) {
//getId() returns different Long objects so the lock does not work
synchronized (myEntity.getId()) {
//the critical section ...
}
}
我读过关于锁对象的文章,但我不确定它们是否适合我的情况。
在顶层,我希望管理对我的应用程序的特定REST调用,该应用程序执行关键代码
谢谢,
Chris问题在于您不应该对值(例如字符串或对象)进行同步
意思:您需要在这里定义一些特殊的EntityId类,当然,所有使用相同ID的“数据”都需要使用相同的EntityId对象 不应将合并并可能重用的对象用于同步。如果是,则会导致不相关的线程死锁,并产生无用的堆栈跟踪 具体来说,
String
文本和装箱原语(如整数
等)应不用作锁对象,因为它们被合并和重用
对于Boolean
对象,情况更糟,因为只有Boolean
的两个实例,Boolean.TRUE
和Boolean.FALSE
,并且每个使用Boolean的类都将引用其中一个
我读过关于锁对象的文章,但我不确定它们是否适合我的应用程序
案例在顶层,我想管理一个特定的REST调用
执行关键代码的应用程序
您将处理并发写入和其他事务性问题。
您所需要做的就是使用事务
我还建议你学习经典问题()。您也可以使用for据我所知,您基本上希望为每个
SomeEntity
ID使用不同的、唯一的锁
您可以通过地图实现这一点
您只需将每个ID映射到一个对象。如果已经存在一个对象,您可以重用它。这可能看起来像这样:
static Map<Integer, Object> locks = new ConcurrentHashMap<>();
public static void main(String[] args)
{
int i1 = 1;
int i2 = 2;
foo(i1);
foo(i1);
foo(i2);
}
public static void foo(int o)
{
synchronized (locks.computeIfAbsent(o, k -> new Object()))
{
// computation
}
}
静态映射锁=新的ConcurrentHashMap();
公共静态void main(字符串[]args)
{
inti1=1;
int i2=2;
foo(i1);
foo(i1);
foo(i2);
}
公共静态无效foo(int o)
{
已同步(locks.computeIfAbsent(o,k->new Object())
{
//计算
}
}
这将在映射中创建2个锁对象,因为在第二个foo(i1)
调用中重用了i1
的对象。private static final Set lockedds=new HashSet();
private static final Set<Integer> lockedIds = new HashSet<>();
private void lock(Integer id) throws InterruptedException {
synchronized (lockedIds) {
while (!lockedIds.add(id)) {
lockedIds.wait();
}
}
}
private void unlock(Integer id) {
synchronized (lockedIds) {
lockedIds.remove(id);
lockedIds.notifyAll();
}
}
public void entityLockedAccess(SomeEntity myEntity) throws InterruptedException {
try {
lock(myEntity.getId());
//Put your code here.
//For different ids it is executed in parallel.
//For equal ids it is executed synchronously.
} finally {
unlock(myEntity.getId());
}
}
私有无效锁(整数id)引发InterruptedException{
已同步(lockedds){
而(!lockedds.add(id)){
lockedds.wait();
}
}
}
私有无效解锁(整数id){
已同步(lockedds){
lockedds.remove(id);
lockedds.notifyAll();
}
}
public void entityLockedAccess(SomeEntity myEntity)引发InterruptedException{
试一试{
锁(myEntity.getId());
//把你的代码放在这里。
//对于不同的ID,它是并行执行的。
//对于相同的ID,它是同步执行的。
}最后{
解锁(myEntity.getId());
}
}
- id不仅可以是“整数”,还可以是具有正确重写的'equals'和'hashCode'方法的任何类
- 最后重试-非常重要-您必须保证在操作后解锁等待的线程,即使操作引发异常
- 如果您的后端分布在多个服务器/JVM上,那么它将不起作用
只需使用此类:
(地图的大小不会随时间而增加)
import java.util.concurrent.ConcurrentHashMap;
导入java.util.function.Consumer;
公共类SameKeySynchronizer{
private final ConcurrentHashMap sameKeyTasks=新ConcurrentHashMap();
公共密钥(T密钥、消费者密钥消费者){
//此映射永远不会被填充(因为函数返回null),它仅用于同一密钥的同步目的
sameKeyTasks.ComputeFabSent(key,inputArgumentKey->acceptReturningNull(inputArgumentKey,keyConsumer));
}
私有对象acceptReturningNull(T inputArgumentKey,Consumer keyConsumer){
接受(inputArgumentKey);
返回null;
}
}
就像在这个测试中:
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SameKeySynchronizerTest {
private static final boolean SHOW_FAILING_TEST = false;
@Test
void sameKeysAreNotExecutedParallel() throws InterruptedException {
TestService testService = new TestService();
TestServiceThread testServiceThread1 = new TestServiceThread(testService, "a");
TestServiceThread testServiceThread2 = new TestServiceThread(testService, "a");
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
Assertions.assertFalse(testService.sameKeyInProgressSimultaneously);
}
@Test
void differentKeysAreExecutedParallel() throws InterruptedException {
TestService testService = new TestService();
TestServiceThread testServiceThread1 = new TestServiceThread(testService, "a");
TestServiceThread testServiceThread2 = new TestServiceThread(testService, "b");
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
Assertions.assertFalse(testService.sameKeyInProgressSimultaneously);
Assertions.assertTrue(testService.differentKeysInProgressSimultaneously);
}
private class TestServiceThread extends Thread {
TestService testService;
String key;
TestServiceThread(TestService testService, String key) {
this.testService = testService;
this.key = key;
}
@Override
public void run() {
testService.process(key);
}
}
private class TestService {
private final SameKeySynchronizer<String> sameKeySynchronizer = new SameKeySynchronizer<>();
private Set<String> keysInProgress = ConcurrentHashMap.newKeySet();
private boolean sameKeyInProgressSimultaneously = false;
private boolean differentKeysInProgressSimultaneously = false;
void process(String key) {
if (SHOW_FAILING_TEST) {
processInternal(key);
} else {
sameKeySynchronizer.serializeSameKeys(key, inputArgumentKey -> processInternal(inputArgumentKey));
}
}
@SuppressWarnings("MagicNumber")
private void processInternal(String key) {
try {
boolean keyInProgress = !keysInProgress.add(key);
if (keyInProgress) {
sameKeyInProgressSimultaneously = true;
}
try {
int sleepTimeInMillis = 100;
for (long elapsedTimeInMillis = 0; elapsedTimeInMillis < 1000; elapsedTimeInMillis += sleepTimeInMillis) {
Thread.sleep(sleepTimeInMillis);
if (keysInProgress.size() > 1) {
differentKeysInProgressSimultaneously = true;
}
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
} finally {
keysInProgress.remove(key);
}
}
}
}
import java.util.Set;
导入java.util.concurrent.ConcurrentHashMap;
导入org.junit.jupiter.api.Assertions;
导入org.junit.jupiter.api.Test;
类SameKeySynchronizerTest{
私有静态最终布尔值SHOW_failed_TEST=false;
@试验
void sameKeySarenoteExecutedParallel()引发InterruptedException{
TestService TestService=新的TestService();
TestServiceThread testServiceThread1=新的TestServiceThread(testService,“a”);
TestServiceThread testServiceThread2=新的TestServiceThread(testService,“a”);
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
assertFalse(testService.sameKeyInProgressSynchronized);
}
@试验
void differentKeysAreExecutedParallel()引发InterruptedException{
TestService TestService=新的TestService();
TestServiceThread
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class SameKeySynchronizerTest {
private static final boolean SHOW_FAILING_TEST = false;
@Test
void sameKeysAreNotExecutedParallel() throws InterruptedException {
TestService testService = new TestService();
TestServiceThread testServiceThread1 = new TestServiceThread(testService, "a");
TestServiceThread testServiceThread2 = new TestServiceThread(testService, "a");
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
Assertions.assertFalse(testService.sameKeyInProgressSimultaneously);
}
@Test
void differentKeysAreExecutedParallel() throws InterruptedException {
TestService testService = new TestService();
TestServiceThread testServiceThread1 = new TestServiceThread(testService, "a");
TestServiceThread testServiceThread2 = new TestServiceThread(testService, "b");
testServiceThread1.start();
testServiceThread2.start();
testServiceThread1.join();
testServiceThread2.join();
Assertions.assertFalse(testService.sameKeyInProgressSimultaneously);
Assertions.assertTrue(testService.differentKeysInProgressSimultaneously);
}
private class TestServiceThread extends Thread {
TestService testService;
String key;
TestServiceThread(TestService testService, String key) {
this.testService = testService;
this.key = key;
}
@Override
public void run() {
testService.process(key);
}
}
private class TestService {
private final SameKeySynchronizer<String> sameKeySynchronizer = new SameKeySynchronizer<>();
private Set<String> keysInProgress = ConcurrentHashMap.newKeySet();
private boolean sameKeyInProgressSimultaneously = false;
private boolean differentKeysInProgressSimultaneously = false;
void process(String key) {
if (SHOW_FAILING_TEST) {
processInternal(key);
} else {
sameKeySynchronizer.serializeSameKeys(key, inputArgumentKey -> processInternal(inputArgumentKey));
}
}
@SuppressWarnings("MagicNumber")
private void processInternal(String key) {
try {
boolean keyInProgress = !keysInProgress.add(key);
if (keyInProgress) {
sameKeyInProgressSimultaneously = true;
}
try {
int sleepTimeInMillis = 100;
for (long elapsedTimeInMillis = 0; elapsedTimeInMillis < 1000; elapsedTimeInMillis += sleepTimeInMillis) {
Thread.sleep(sleepTimeInMillis);
if (keysInProgress.size() > 1) {
differentKeysInProgressSimultaneously = true;
}
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
} finally {
keysInProgress.remove(key);
}
}
}
}