Это хороший дизайн для реализации Java синхронизированного ключевого слова в качестве объекта?
Просто для практики я хотел реализовать синхронизированное ключевое слово java как объект java. Вы сказали бы, что код ниже - хороший дизайн для этого? Я думаю, AtomicReference будет иметь аналогичную производительность для AtomicBoolean?
Обновлен код после предложений:
public class SynchronizedBlock implements Runnable{
private final Lock lock;
private final Runnable runnable;
public SynchronizedBlock(Runnable r, Lock l){
runnable = r;
lock = l;
}
public void run() {
try {
while(!lock.compareAndSet(false, true)){
Thread.sleep(100);
}
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class Lock {
private final AtomicReference<Boolean> locked = new AtomicReference<Boolean>(false);
public boolean compareAndSet(boolean expected, boolean update){
return locked.compareAndSet(expected, update);
}
public boolean isLocked(){
return locked.get();
}
public void unlock(){
locked.set(false);
}
}
@Test
public void test() {
final SynchronizedBlock sb = new SynchronizedBlock(new Runnable(){
public void run() {
x++;
System.out.println(x);
}
}, new Lock());
Runnable r1 = new Runnable(){
int c = 0;
public void run() {
while(c<10){
sb.run();
c++;
}
}
};
Runnable r2 = new Runnable(){
int c = 0;
public void run() {
while(c<10){
sb.run();
c++;
}
}
};
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
while (t1.isAlive() && t2.isAlive()){
}
assertEquals(20,x);
}
2 ответа
Вы должны добавить метод для инкапсуляции compareAndSwap, и нет никакой точки зацикливания для освобождения блокировки, прежде чем пытаться получить ее. Зачем попадать в ситуацию, когда вы видите, что замок свободен, но к тому времени, когда вы пытаетесь его взять, он исчезает.
Я бы удалил метод блокировки и поместил разблокировку в блокировку finally, чтобы исключение / ошибка не приводили к блокировке, которая никогда не разблокируется.
Также я бы использовал AtomicBoolean, который является более естественным, чем AtomicReference
Во-первых и самое главное, вы должны удалить Thread.sleep(100)
, Это приведет к задержке не менее 100 мс даже в конфликте только с двумя потоками.
Вы можете просто использовать AtomicBoolean
вместо AtomicReference
упростить ваш код. Также, если вы действительно обеспокоены параллелизмом в ситуации высокой конкуренции, вы можете изменить свой код, чтобы проверить, не заблокирован ли он перед выполнением CAS.
while (true) {
if (lock.isLocked()) continue; // or get() == true if you use AtomicBoolean
if (lock.compareAndSet(false, true))
break;
}
Это пример блокировки TTAS(Test-Test-And-Set), которая использует преимущества локального вращения для уменьшения доступа к основной памяти во время цикла. Смотрите http://en.wikipedia.org/wiki/Test_and_Test-and-set