Потоки Java: синхронизированные блоки
Мне нужна помощь, чтобы убедиться, что я понимаю синхронизированные блоки. Предполагая следующий пример:
public class ThreadStarter {
public static void main(String[] args) {
Queue queueObject = new Queue();
ThreadA thread1 = new ThreadA(queueObject);
ThreadA thread2 = new ThreadA(queueObject);
ThreadB thread3 = new ThreadB(queueObject);
ThreadB thread4 = new ThreadB(queueObject);
thread1.start();
thread2.start();
}
}
public class Queue {
Object[] the theQueue;
public Queue(int size){
theQueue = new Object[size];
}
public submitObject(Object o){
/* add Object to the queue */
}
public deleteObject(int objectId){
/*delete Object from the queue */
}
}
public class ThreadA extends Thread {
private Queue queue;
public ThreadA(Queue queue){
this.queue = queue;
}
public void run() {
while(!isInterrupted()){
synchronized(queue) {
Object o = new Object
queue.submitObject(o);
/* do some other stuff */
}
try {
sleep((int)(Math.random()*1000));
} catch (interruptedException) {
Thread.currentThread().interrupt;
}
synchronized(queue) {
/* do some other stuff on the queue */
}
}
}
}
public class ThreadB extends Thread {
private Queue queue;
public ThreadB(Queue queue){
this.queue = queue;
}
public void run() {
while(!isInterrupted()){
synchronized(queue) {
queue.deleteObject(o);
/* do some other stuff */
}
try {
sleep(1000);
} catch (interruptedException) {
Thread.currentThread().interrupt;
}
}
}
}
Мой вопрос: достаточно ли синхронизировать весь объект очереди в ThreadA, чтобы передать объект в класс очереди, чтобы быть в безопасности? Я сделал то же самое в ThreadB, чтобы удалить объект из очереди. Или мне нужно синхронизировать метод submitObject() и deleteObject() в классе Queue?
В моем понимании, если я заблокирую весь класс Queue в потоках, как показано выше, я должен быть в безопасности - правильно?
greetZ и заранее спасибо.
3 ответа
Все, что вам нужно сделать здесь, это убедиться, что никакие два потока не могут попасть внутрь submitObject и deleteObjecct одновременно. Для этого просто объявите эти методы синхронизированными. В этом случае объект очереди, который является общим для классов, не позволит двум потокам работать в блоке, который он синхронизирует вместе.
Кроме того, если вы хотите иметь какой-то механизм блокировки, такой как:
"если поток хочет удалить объект, он должен ждать, если в очереди нет такого объекта".
тогда вам нужно сделать больше, чем просто синхронизировать таким образом: оба метода должны быть синхронизированы, но поток, входящий в deleteObject, должен быть остановлен с помощью this.wait в очереди, пока объект не станет доступным:
public synchronized deleteObject() {
while( isEmpty() ) {
try {
wait();
} catch( Exception ex ) {
ex.printStackTrace();
}//catch
}//while
//actually do delete object.
...
}//met
и затем submitObject должен уведомить потоки в состоянии ожидания, выполнив:
public synchronized submitObject() {
//do put object
...
notifyAll();
}//met
и вы также можете пересекать роли и добавлять некоторый код в обоих методах, например, чтобы разрешить блокировку отправителя, если очередь заполнена, и получать уведомления, когда в очереди осталось место.
Я бы синхронизировал по очереди объект в submitObject
а также deleteObject
методов и этого должно быть достаточно, а это означает, что:
public submitObject(Object o){
synchronized (theQueue) {
...
}
}
public deleteObject(int objectId){
synchronized (theQueue) {
...
}
}
То, что вы делаете, эквивалентно синхронизации методов (public synchronized method()
синхронизируется с this
какой твой queue
локальная переменная), за исключением того, что вы должны помнить, что вам нужно делать это каждый раз, когда вы используете очередь. Было бы безопаснее синхронизировать методы отправки и удаления. Object o = new Object()
из синхронизированного блока.