使用Locks和synchronized的组合会导致意外结果

时间:2013-12-03 00:44:05

标签: java multithreading locking synchronized

Driver.java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Driver {

    static Lock lock = new ReentrantLock();
    static Integer incr = 20;

    public static void main(String [] arg) throws InterruptedException
    {
        Thread thr1 = new Thread(new Runnable(){
            @Override
            public void run() {
                lock.lock();
                System.out.println("Gained access! - 1");
                try {Thread.sleep(5000);} catch (InterruptedException e) {}
                incr++;
                lock.unlock();
            }
        });

        Thread thr2 = new Thread(new Runnable(){
            @Override
            public void run() {
                lock.lock();
                System.out.println("Gained access! - 2");
                incr++;
                lock.unlock();
            }
        });

        Thread thr3 = new Thread(new Runnable(){
            @Override
            public void run() {
                synchronized(incr){                 
                    System.out.println("Gained access! - 3");
                    try {Thread.sleep(5000);} catch (InterruptedException e) {}
                    incr++;
                }
            }
        });

        Thread thr4 = new Thread(new Runnable(){
            @Override
            public void run() {
                synchronized(incr){
                    System.out.println("Gained access! - 4");
                    incr++;
                }
            }
        });

        thr1.start();
        thr2.start();
        thr3.start();
        thr4.start();

        thr1.join();
        thr2.join();
        thr3.join();
        thr4.join();

        System.out.println(incr);
    }
}

运行1 - 输出

Gained access! - 3
Gained access! - 2
Gained access! - 1
Gained access! - 4
23

运行2 - 输出

Gained access! - 1
Gained access! - 4
Gained access! - 3
Gained access! - 2
24

运行N - 输出

Switching orders of Thread execution. Seen it hit a sum of 22.

问题

我正在尝试进行简单的可重入锁定和同步练习。

  1. 这些输出是否表明ReentrantLock内部正在使用synchronized
  2. 这些锁定/同步基本上是while循环,直到变量可访问或执行实际暂停(在字节码中),直到对象/锁定被解锁?
  3. 为什么上面的总和不总是24?线程执行以下操作:

      

    主题1:Lock | 5s等待|值++
      线程2:Lock | 0s等待|值++
      主题3:syncronized | 5s等待|值++
      主题4:syncronized | 0s等待|值++

  4. 4。有人可以确认这基本上是synchronized内部正在做什么吗?我对synchronized内部运作的了解很少。这是我认为可能是复制synchronized的合理方式。所以,我不知道这是不是正确的方法。所以请告诉我Java中的内部工作方式是否与此不同。

    public class Driver {
    
        static Safe<Integer> value = new Safe<Integer>(20);
    
        public static void main(String [] arg) throws InterruptedException
        {
            Thread thr1 = new Thread(new Runnable(){
                @Override
                public void run() {             
                    value.lock();
                    System.out.println("Gained access! - 1");
                    value.data++;
                    try {Thread.sleep(5000);} catch (InterruptedException e) {}
                    value.unlock();
                }
            });
    
            Thread thr2 = new Thread(new Runnable(){
                @Override
                public void run() {
                    value.lock();
                    System.out.println("Gained access! - 2");
                    value.data++;
                    try {Thread.sleep(5000);} catch (InterruptedException e) {}
                    value.unlock();
                }
            });
    
    
            thr1.start();
            thr2.start();
    
            thr1.join();
            thr2.join();
    
            System.out.println(value);
        }
    }
    
    class Safe<E>{
        private volatile boolean lock = false;
        protected E data;
    
        public Safe(E d)
        {
            data = d;
        }
        public String toString()
        {
            return data.toString();
        }
        public void lock()
        {
            while(isLocked()){}
            lock = true;
        }
        public void unlock()
        {
            lock = false;
        }
        public boolean isLocked()
        {
            return lock;
        }
    }
    

    谢谢!

1 个答案:

答案 0 :(得分:1)

  1. 他们可能,但这个结果并没有这么说。你为什么那么想?无论哪种方式the synchronized statement uses monitorenter and monitorexit,它使用this(或其编码)作为条件变量,而后者又与您的ReentrantLock不同。线程总是以不同顺序执行的原因仅仅在于执行顺序取决于系统调度程序。线程排队等待创建的顺序通常不是它们第一次运行的顺序。
  2. 如果您追溯Java框架代码,您会发现最终会调用Unsafe.park,其实现与系统有关。这意味着,除非有人能够生成陈述实施策略的官方Java文档,否则我们不知道它是否使用"busy waiting"(在锁定可用之前循环),wait-notify或两者的混合,以及它可能(并且可能确实)在不同系统和Java版本之间有所不同。
  3. 您的总和并不总是正确的,因为您有一个竞争条件,因为synchronizedlock()锁定了不同的信号量。具体来说,线程1和2彼此同步,但不与3和4同步。因为你有两个同步的增量,你总是会看到至少两个增量(因此总和至少是22)。