如果一个同步方法调用另一个非同步方法,非同步方法是不是有锁
Posted
技术标签:
【中文标题】如果一个同步方法调用另一个非同步方法,非同步方法是不是有锁【英文标题】: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中,如果一个同步方法包含对一个非同步方法的调用,那么另一个方法还能同时访问非同步方法吗?基本上我要问的是同步方法中的所有内容都有锁定(包括对其他同步方法的调用)?
【问题讨论】:
【参考方案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 类的锁部分没有被注释掉,那么就不会交错,首先获得锁的线程会先完全完成,然后它将释放锁,然后另一个线程可以继续。
【讨论】:
以上是关于如果一个同步方法调用另一个非同步方法,非同步方法是不是有锁的主要内容,如果未能解决你的问题,请参考以下文章