2015-02-10 2 views
0

У меня есть служба, к которой могут обращаться различные темы. Служба имеет 3 лонги и два метода - метод, который добавляет их все вместе и метод, который изменяет средний один к 0.Синхронизировать два разных метода с помощью eachother или синхронизировать переменные

private long a = 10; 
private long b = 10; 
private long c = 10; 

public long add(){ 
    return a + b + c; 
} 

public void foo(){ 
    b = 0; 
} 

Теперь, конечно, код становится более сложным, но дело в том, что в то время как на нити доступа add() другой поток может получить доступ к методу foo() и изменить значение b, поэтому im никогда не уверен, каким будет результат. Как я могу синхронизировать эти два метода или синхронизировать переменную a, b, c, чтобы это было безопасным потоком?

+0

Вы не можете синхронизировать переменные. Просто так вы знаете. –

+0

@ DeepakBala, что вы говорите, технически верно, но не забывайте, что вся точка взаимного исключения заключается в защите _data_. Мы делаем это на Java, бросая «синхронизированный» блок вокруг _every place_, где данные модифицируются _или_. Мы используем 'private' в Java, чтобы ограничить количество мест, где эта синхронизация необходима. –

ответ

4

Если вы сделаете методы synchronized, тогда только один будет разрешен для выполнения на объекте в любой момент времени. Если у вас несколько экземпляров этого класса, и они не являются статическими, то каждый экземпляр может иметь исполняемый метод. В следующем примере, если add и foo называются в то же время, один из них будет ждать:

public synchronized long add() { 
    return a + b + c; 
} 

public synchronized void foo() { 
    b = 0; 
} 

Примечание: Синхронизация не сочиняет. То есть, объединение двух synchronized вещей не то же самое, что объединение двух вещей, а затем добавление synchronized. Допустим, у вас synchronized методы getA, getB, getC, то это:

public long add2() { 
    return getA() + getB() + getC(); 
} 

бы не быть идентичен старому add методу, потому что другой поток мог бы назвать foo в между вызовами getA и getB.

2

Вы можете объявить методы, как синхронизируются:

public synchronized long add(){ 
    return a + b + c; 
} 

public synchronized void foo(){ 
    b = 0; 
} 

См Synchronized Methods.

2

способ сделать это будет иметь другой object сказать

private object elock = new byte[0]; 

, а затем в каждом из этих двух методов огибающей критической секции с

synchronized(elock) {/* critical section */} 

Как и в

public long add(){ 
    synchronized(elock) { 
    return a + b + c; 
    } 
} 

public void foo(){ 
    synchronized(elock) { 
     b = 0; 
    } 
} 

Доступны более оптимальные схемы блокировки, но, поскольку вы спросили в целом, вышеуказанное должно быть достаточно.

1

Вы можете сделать все это потокобезопасный так:

private long a = 10; 
private long b = 10; 
private long c = 10; 

public synchronized long add(){ 
    return a + b + c; 
} 

public synchronized void foo(){ 
    b = 0; 
} 

Маркировка нестатический метод как synchronized имеет следующий эффект:

public synchronized void a() { 
    // do something 
} 
// is the same as: 
public void a() { 
    synchronized(this) { 
    // do something 
    } 
} 

И поскольку никакие две нити могут когда-либо блокировка на одном и том же объекте одновременно (в этом случае объект this), если один поток вызывает a(), затем другой вызывает b(), второй поток должен ждать, пока первый поток не покинет a().

Вы можете узнать больше об этой теме here.

Обратите внимание, что в вашем примере это не достаточно объявить только один метод, как synchronized, даже если «только-один-нить-на-время» поведение достаточно для одного из методов. Это из-за Java Memory Model, который является темой, на которую вы должны взглянуть, как только узнаете все о многопоточности в Java.

2

У вас есть четыре ответа до сих пор, все говорят вам одно и то же: синхронизировать оба метода. Но вот что: Даже с, что синхронизация, вы все равно никогда не будете уверены, какой результат будет. У вас есть гонка данных между потоком, который вызывает add() и поток, который вызывает foo(). Ответ, возвращаемый add(), будет зависеть от того, выиграет он или проиграет гонку.

Фактически, в этом конкретном примере синхронизация не добавляет никакого значения вообще.

С синхронизацией гарантируется, что вызов метода foo() и вызов метода add() не будут перекрываться. Вы можете использовать это знание, чтобы доказать, что add() либо вернет 20 (если он проиграет гонку), либо 30, если выиграет гонка.

Но факт в этом конкретном примере из-за того, как работают JVM, add() всегда возвращал либо 20, либо 30 в любом случае.

Если в расчете было больше шагов, особенно если оно было зациклировано и указано b более одного раза, то синхронизация имела бы значение. При синхронизации будет только два возможных ответа, но без синхронизации возможны другие результаты.

+0

Я полностью согласен с этим. Механизм блокировки должен быть реализован в вызывающих потоках для предотвращения гонки. – Sven

Смежные вопросы