为了账号安全,请及时绑定邮箱和手机立即绑定

什么时候应该使用同步方法,什么时候应该在 Java 中使用同步块?

什么时候应该使用同步方法,什么时候应该在 Java 中使用同步块?

胡说叔叔 2022-05-21 13:07:16
我正在研究如何在 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);    }}
查看完整描述

2 回答

?
慕森王

TA贡献1777条经验 获得超3个赞

我不认为这是一个骗局,因为尽管有标题,但实际问题是 OP 的具体实现。代码中有一个错误,这不是方法与块的问题。


代码中的错误是您尝试实现锁定机制的地方。在incr()中,您等到available设置为 false,这仅发生在getVal():


public synchronized void incr() {

    while (available) { // <-- bug

        try {

            wait();

由于您的循环只调用incr()而不调用getVal(),因此两个线程在第一次调用incr(). (你getVal()最终会调用,但只有在内循环完成之后。两个线程都很好并且到那时卡住了。)


解决方案:AtomicInteger没有像这样的奇怪错误。如果您尝试实现某种生产者/消费者机制,那么其中一个并发队列(如ArrayBlockingQueue)是更好的解决方案。


查看完整回答
反对 回复 2022-05-21
?
jeck猫

TA贡献1909条经验 获得超7个赞

  1. 同步方法之间的一个显着区别是,同步块通常会减少锁定范围。由于锁定范围与性能成反比,因此只锁定代码的关键部分总是更好。使用同步块的最佳示例之一是 Singleton 模式中的双重检查锁定,getInstance()我们只锁定用于创建 Singleton 实例的代码的关键部分,而不是锁定整个方法。这极大地提高了性能,因为锁定只需要一两次。

  2. 同步块提供了对锁的精细控制,因为您可以使用任意锁来为关键部分代码提供互斥。另一方面,同步方法总是锁定由 this 关键字表示的当前对象或类级别锁定,如果它的静态同步方法。

  3. 如果作为参数提供给块的表达式计算为 null,则同步块可以抛出 throw java.lang.NullPointerException,而同步方法则不是这种情况。

  4. 在同步方法的情况下,线程进入方法时获取锁,离开方法时释放锁,正常或抛出异常。另一方面,在同步块的情况下,线程进入同步块时获取锁,离开同步块时释放。


查看完整回答
反对 回复 2022-05-21
  • 2 回答
  • 0 关注
  • 121 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信