代码之家  ›  专栏  ›  技术社区  ›  NoProg

何时应该使用同步方法,何时使用Java中的同步块?

  •  0
  • NoProg  · 技术社区  · 6 年前

    我正在研究如何同步爪哇中的方法和块,以避免竞争条件,我试图以两种方式解决练习。 问题是,如果我尝试使用synchronized块,所有东西都可以正常工作,但使用synchronized方法会卡住。 我想我可以用两种方式,但差别不大(在某些情况下可能其中一种会降低并行性,但我不确定这一点)。我想知道我的代码有什么问题,我想问一下,在什么情况下,使用同步块而不是同步方法更可取。

    /不工作

    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);
        }
    }
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   markspace    6 年前

    我不认为这是一个重复,因为,尽管标题,实际问题是操作的具体实现。代码中有一个bug,这不是方法和块的问题。

    代码中的bug是您试图实现锁定机制的地方。在 incr() ,你等到 available 设置为false,仅在 getVal() :

    public synchronized void incr() {
        while (available) { // <-- bug
            try {
                wait();
    

    因为你的循环只调用 输入() 不打电话 GETVAL() ,两个线程在第一次调用 输入() . (你打电话 GETVAL() 最终,但只有在内部循环完成之后。两条线都很好,然后就卡住了。)

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

        2
  •  0
  •   TheSprinter    6 年前
    1. 两者之间的一个显著差异 同步方法 也就是说,同步块通常会缩小锁的范围。由于锁的范围与性能成反比,所以最好只锁定代码的关键部分。使用synchronized块的最佳示例之一是以单例模式进行双重检查锁定,在这种模式中,不锁定整个块。 getInstance() 方法只锁定用于创建单例实例的代码的关键部分。这大大提高了性能,因为只需要一到两次锁定。

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

    3. 同步块可以投掷 java.lang.NullPointerException 提供给block as参数的if表达式的计算结果为空,这与synchronized方法不同。

    4. 在同步方法的情况下,线程在进入方法时获取锁,在离开方法时释放锁,正常情况下或通过引发异常来获取锁。另一方面,对于同步块,线程在进入同步块时获取锁,在离开同步块时释放锁。