[英]When should I use a synchronized method and when a synchronized block in Java?
我正在研究如何在Java中同步方法和块,以避免出现竞争状况,并且我尝试以两种方式解决一个问题。 问题是,如果我尝试使用同步块,则一切正常,但是使用同步方法时,它会卡住。 我以为我可以在没有太大差异的情况下使用两种方式(也许其中一种在某些情况下会降低并行度,但是我不确定)。 我想知道代码中有什么问题,我想问一下是否有使用同步块而不是同步方法的情况更可取。
//不起作用
import java.util.Random;
class MultiplicationTable extends Thread {
private Cont obj;
private int number;
private Random r;
public MultiplicationTable(Cont o, int num) {
obj = o;
number = num;
r = new Random();
start();
}
public void run() {
for (int j = 0; j < 10; j++) {
for (int i = 0; i < number; i++) {
obj.incr();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + ": " + obj.getVal());
}
try {
Thread.sleep(r.nextInt(2000));
} catch (InterruptedException e) {
}
}
}
class Cont {
private int count = 0;
private boolean available = false;
public synchronized void incr() {
while (available) {
try {
wait();
} catch (InterruptedException e) {
// TODO: handle exception
}
}
available = true;
count++;
notifyAll();
}
public synchronized int getVal() {
while (!available) {
try {
wait();
} catch (Exception e) {
// TODO: handle exception
}
}
available = false;
notifyAll();
return count;
}
}
public class Es3 {
public static void main(String[] args) {
Cont obj = new Cont();
int num = 5;
MultiplicationTable t1 = new MultiplicationTable(obj, num);
MultiplicationTable t2 = new MultiplicationTable(obj, num);
}
}
//工作
import java.util.Random;
class MultiplicationTable extends Thread {
private Cont obj;
private int number;
private Random r;
public MultiplicationTable(Cont o, int num) {
obj = o;
number = num;
r = new Random();
start();
}
public void run() {
synchronized (obj) {
for (int j = 0; j < 10; j++) {
for (int i = 0; i < number; i++) {
obj.incr();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
System.out.println(Thread.currentThread().getName() + ": " + obj.getVal());
}
try {
Thread.sleep(r.nextInt(2000));
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
}
class Cont {
private int count = 0;
public void incr() {
count++;
}
public int getVal() {
return count;
}
}
public class Es3 {
public static void main(String[] args) {
Cont obj = new Cont();
int num = 5;
MultiplicationTable t1 = new MultiplicationTable(obj, num);
MultiplicationTable t2 = new MultiplicationTable(obj, num);
}
}
我不认为这是骗人的,因为尽管有标题,但实际的问题是OP的特定实现。 代码中有一个错误,这不是方法与块的问题。
代码中的错误是您尝试实现锁定机制的地方。 在incr()
,您等待直到将available
设置为false,这仅在getVal()
发生:
public synchronized void incr() {
while (available) { // <-- bug
try {
wait();
由于您的循环仅调用incr()
而不调用getVal()
,因此在第一次调用incr()
之后,两个线程都被卡住。 (您最终将调用getVal()
,但仅在内部循环完成之后才调用。两个线程都处于良好状态,并且到那时为止都处于阻塞状态。)
解决方案: AtomicInteger
没有像这样的怪异错误。 如果您尝试实现某种生产者/消费者机制,那么并发队列之一(例如ArrayBlockingQueue
)是更好的解决方案。
同步方法和块之间的一个重要区别是,同步块通常会减小锁定范围。 由于锁定范围与性能成反比,因此锁定仅关键部分的代码总是更好。 使用同步块的最佳示例之一是在Singleton模式中进行双重检查锁定,在此锁定中,不是锁定整个getInstance()
方法,而是仅锁定用于创建Singleton实例的代码的关键部分。 由于仅需要锁定一到两次,因此可以大大提高性能。
同步块提供了对锁的精细控制,因为您可以使用任意任意锁来对关键节代码提供互斥。 另一方面,如果同步方法是静态同步方法,则始终锁定此关键字表示的当前对象或类级别锁。
如果作为参数提供给block的表达式的计算结果为null,则同步块会引发java.lang.NullPointerException
异常,而同步方法则不是这种情况。
在使用同步方法的情况下,锁通常在进入状态时通过线程获取,而在退出方法时则通过抛出Exception释放。 另一方面,在同步块的情况下,线程在进入同步块时获得锁定,而在离开同步块时释放。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.