【发布时间】:2012-03-20 12:26:46
【问题描述】:
在Java中,如果一个同步方法包含对一个非同步方法的调用,那么另一个方法还能同时访问非同步方法吗?基本上我要问的是同步方法中的所有内容都有锁定(包括对其他同步方法的调用)?
【问题讨论】:
标签: java multithreading methods locking synchronized
在Java中,如果一个同步方法包含对一个非同步方法的调用,那么另一个方法还能同时访问非同步方法吗?基本上我要问的是同步方法中的所有内容都有锁定(包括对其他同步方法的调用)?
【问题讨论】:
标签: java multithreading methods locking synchronized
如果一个同步方法调用另一个非同步方法,非同步方法是否有锁
答案取决于上下文。
如果您在一个对象的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 方法进行操作,则其中一个线程将阻塞,直到另一个线程退出该方法。
【讨论】:
toString() 和其他方法总是会发生这种情况。这仅取决于您的对象和用例。
如果线程 A 调用同步方法 M1,而后者又调用非同步方法 M2,那么线程 B 仍然可以调用 M2 而不会阻塞。
同步方法在调用它的对象上获取和释放内在锁。这就是它可能会阻塞的原因。非同步方法不会尝试获取任何锁(除非在代码中明确完成)。
因此,如果您还需要确保 M2 的互斥,则无论其调用方(如 M1)是否同步,都应使其同步。
【讨论】:
锁属于线程,而不属于方法(或更准确地说,它的堆栈帧)。碰巧的是,如果你有一个同步方法,你可以保证线程在方法体开始之前拥有锁,然后释放它。
另一个线程仍然可以调用第二个非同步方法。任何线程都可以随时调用非同步方法。
【讨论】:
锁不属于线程。锁实际上属于对象(或类级别锁的情况下的类),并且线程在同步上下文中获取对象(或类级别锁的情况下的类)上的锁。 现在,正如上面讨论的那样,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 类的锁部分没有被注释掉,那么就不会交错,首先获得锁的线程会先完全完成,然后它将释放锁,然后另一个线程可以继续。
【讨论】: