【问题标题】:If a synchronized method calls another non-synchronized method, is there a lock on the non-synchronized method如果一个同步方法调用另一个非同步方法,非同步方法是否有锁
【发布时间】:2012-03-20 12:26:46
【问题描述】:

在Java中,如果一个同步方法包含对一个非同步方法的调用,那么另一个方法还能同时访问非同步方法吗?基本上我要问的是同步方法中的所有内容都有锁定(包括对其他同步方法的调用)?

【问题讨论】:

    标签: java multithreading methods locking synchronized


    【解决方案1】:

    如果一个同步方法调用另一个非同步方法,非同步方法是否有锁

    答案取决于上下文。

    如果您在一个对象的synchronized 方法中,则其他线程对同样为synchronized 的同一对象实例的其他方法的调用将被锁定。然而,其他线程对非同步方法的调用是锁定的——任何人都可以同时调用它们。

    public synchronized void someSynchronizedMethod() {
        ...
        someNonSynchronizedMethod();
        ...
    }
    
    // anyone can call this method even if the someSynchronizedMethod() method has
    // been called and the lock has been locked
    public void someNonSynchronizedMethod() {
       ...
    }
    

    此外,如果您调用 someSynchronizedMethod() 但恰好在 someNonSynchronizedMethod() 方法内,您仍然持有锁。进入同步方法(或块)时启用锁,退出该方法时禁用。你可以调用其他各种不同步的方法,它们仍然会被锁定。

    但是您在问题中提出了两个不同的问题:

    在Java中,如果一个同步方法包含对一个非同步方法的调用,那么另一个方法还能同时访问非同步方法吗?

    是的。其他方法可以访问非同步方法。

    基本上我要问的是同步方法中的所有内容都有锁定(包括对其他同步方法的调用)?

    嗯,是的。对同步方法的其他调用被锁定。但非同步方法不会被锁定。

    另外,请记住,如果方法是 static,则锁定在 ClassLoader 中的 Class 对象上。

    // this locks on the Class object in the ClassLoader
    public static synchronized void someStaticMethod() {
    

    如果方法是实例方法,那么锁就在类的实例上。

    // this locks on the instance object that contains the method
    public synchronized void someInstanceMethod() {
    

    这两种情况下有两种不同的锁。

    最后,当您处理synchronized 实例方法时,该类的每个instance 都是被锁定的。这意味着两个线程可以同时在同一个synchronized 方法中使用不同的instances。但是如果 2 个线程尝试对同一实例上的 synchronized 方法进行操作,则其中一个线程将阻塞,直到另一个线程退出该方法。

    【讨论】:

    • 所以我想在同步方法中调用非同步方法不是一个好主意……因为它们可以同时被其他方法调用。
    • 如果这导致2个线程访问需要同步的数据,那么是的,这不是一个好主意。但是,toString() 和其他方法总是会发生这种情况。这仅取决于您的对象和用例。
    • 我还想在这里补充一点,只要在不同的对象上调用同步方法,就可以同时调用任意次数。锁适用于特定对象——类的实例——除非方法是静态的。我认为很容易忘记这一点,因为很多人(包括所有当前的发帖人)都说方法上的锁定,就好像不管对象实例如何都应用它一样。
    【解决方案2】:

    如果线程 A 调用同步方法 M1,而后者又调用非同步方法 M2,那么线程 B 仍然可以调用 M2 而不会阻塞。

    同步方法在调用它的对象上获取和释放内在锁。这就是它可能会阻塞的原因。非同步方法不会尝试获取任何锁(除非在代码中明确完成)。

    因此,如果您还需要确保 M2 的互斥,则无论其调用方(如 M1)是否同步,都应使其同步。

    【讨论】:

      【解决方案3】:

      锁属于线程,而不属于方法(或更准确地说,它的堆栈帧)。碰巧的是,如果你有一个同步方法,你可以保证线程在方法体开始之前拥有锁,然后释放它。

      另一个线程仍然可以调用第二个非同步方法。任何线程都可以随时调用非同步方法。

      【讨论】:

        【解决方案4】:

        锁不属于线程。锁实际上属于对象(或类级别锁的情况下的类),并且线程在同步上下文中获取对象(或类级别锁的情况下的类)上的锁。 现在,正如上面讨论的那样,Java 中没有锁传播。这是一个小演示:

        public class TestThread {
        
            /**
             * @param args
             * @throws InterruptedException 
             */
            public static void main(String[] args) throws InterruptedException {
                // TODO Auto-generated method stub
                ThreadCreator1 threadCreator1 = new ThreadCreator1();
                ThreadCreator2 threadCreator2 = new ThreadCreator2();
        
                Thread t1 = new Thread(threadCreator1,"Thread 1");
                Thread t3 = new Thread(threadCreator1,"Thread 3");
                Thread t2 = new Thread(threadCreator2,"Thread 2");
        
                t1.start();
                Thread.sleep(2000);
                t3.start();
        
            }
        }
        
        public class ThreadCreator1 implements Runnable {
        
            private static final Task task= new Task();
            private static final Task2 task2= new Task2();
        
            @Override
        
            public void run() {
        
                try {
        
                    if(Thread.currentThread().getName().equals("Thread 1"))
                        task.startTask2(task2);
                    if(Thread.currentThread().getName().equals("Thread 3"))
                        task2.startTask();
        
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // TODO Auto-generated method stub
        
                /**/
        
                }
            }
        
        
        public class Task {
        
            public static final Task task = new Task();
            public static List<String> dataList = new ArrayList<String>();
            ReentrantLock lock =  new ReentrantLock();
        
        
        
            public  void startTask2(Task2 task2) throws InterruptedException
            {
        
                try{
        
                    lock.lock();
                    //new Task2().startTask();
                    task2.startTask();
                }
                catch(Exception e)
                {
        
                }
                finally{
                    lock.unlock();
                }
            }
        
        }
        
        public class Task2 {
        
            ReentrantLock lock = new ReentrantLock();
            public  void startTask() throws InterruptedException
            {
        
                try{
                    //lock.lock();
                    for(int i =0 ;i< 10;i++)
                {
                    System.out.println(" *** Printing i:"+i+" for:"+Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
                }
                catch(Exception e)
                {
        
                }
                /*finally
                {
                    lock.unlock();
                }*/
            }
        
        }
        

        只是我在这里使用了可重入锁。 如果运行上面的代码,那么线程 1 和线程 3 之间会有交错,但是如果 Task2 类的锁部分没有被注释掉,那么就不会交错,首先获得锁的线程会先完全完成,然后它将释放锁,然后另一个线程可以继续。

        【讨论】:

          猜你喜欢
          • 2011-08-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-07
          • 2014-04-09
          • 2014-01-29
          相关资源
          最近更新 更多