首先需要阐明两个基本问题:1.锁只能获取自对象,而不是代码,也就是说synchronized [static] methodName这样的写法其实获取的还是对象锁;2.未synchronized的代码执行不受锁的影响。
下面看synchronized的使用场景:
1.synchronized非静态方法和synchronized(this)
这是等效的两种写法,也就是说synchronized非静态方法其实获取的是this对象锁。当多个线程操作同一实例时,任一线程调用任一synchronized非静态方法均会阻塞其余线程对所有synchronized非静态方法的调用,也就是说所有线程对synchronized非静态方法的调用将变成串行。当不同线程操作不同实例时,对synchronized非静态方法的调用将变成并行。
class SyncTest
{
public void nonStaticA()
{
synchronized (this)
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " nonStaticA");
Thread.sleep(100);
}
}
}
public synchronized void nonStaticB()
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " nonStaticB");
Thread.sleep(100);
}
}
......
}
假设SyncTest对象obj,线程T1调用obj#nonStaticA,线程T2调用obj#nonStaticB,后者调用的执行必须在前者完成之后。
2.synchronized静态方法和synchronized(class)
这是等效的两种写法,也就是说synchronized静态方法其实获取的是class对象锁。
当多个线程操作任意实例时,任一线程调用任一synchronized静态方法均会阻塞其余线程对所有synchronized静态方法的调用,也就是说所有线程对synchronized静态方法的调用将变成串行。
class SyncTest
{
public void staticA()
{
synchronized (SyncTest.class)
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " staticA");
Thread.sleep(100);
}
}
}
public synchronized static void staticB()
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " staticB");
Thread.sleep(100);
}
}
......
}
假设线程T1调用SyncTest#staticB,线程T2调用obj#staticA,后者调用的执行必须在前者完成之后。
3.synchronized非静态方法和synchronized静态方法共存
当多个线程操作同一实例时,任一线程调用任一synchronized非静态方法均不阻塞其余线程对所有synchronized静态方法的调用,反之亦然,也就是说这两者彼此不影响。
结合上述的SyncTest代码,假设线程T1调用obj#nonStaticB,线程T2调用SyncTest#staticB,两者会并发执行。
4.synchronized非静态成员变量
这个本质与1一样,只不过是锁的作用区域变小了,一般是方法体内的代码块。
当多个线程操作同一实例时,任一线程执行synchronized(lockObj)代码块时,均会阻塞其余线程对所有的synchronized(lockObj)代码块的执行,也就是说所有线程对synchronized(lockObj)代码块的执行将变成串行。注意:此处的synchronized(lockObj)表示获取名称为lockObj的非static对象的锁,如果类代码中还有其余对象锁的话,彼此不受影响。当然,如果不同线程针对不同实例的话,也相互没有影响。
class SyncTest
{
private final Object lock = new Object();
public void lockMethodA()
{
......
synchronized (lock)
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " lockMethodA");
Thread.sleep(100);
}
}
......
}
......
}
假设SyncTest对象obj,线程T1调用obj#lockMethodA,线程T2调用obj#lockMethodA,前者将在synchronized (lock)处阻塞后者。如果T2调用obj#nonStaticB,两个线程彼此没有影响。注意:obj#nonStaticB获取的是this对象锁,obj#lockMethodA获取的是lock对象锁,虽然lock是this的成员变量,但是作为对象锁时彼此没有影响。
5.synchronized静态成员变量
这个本质与2一样,也只不过是锁的作用区域变小为方法体内的代码块。
当多个线程操作任一实例时,任一线程执行synchronized(lockObj)代码块时,均会阻塞其余线程对所有的synchronized(lockObj)代码块的执行,也就是说所有线程对synchronized(lockObj)代码块的执行将变成串行。注意:此处的synchronized(lockObj)表示获取名称为lockObj的static对象的锁,如果类代码中还有其余对象锁的话,彼此不受影响。
class SyncTest
{
private final static Object lockStaticA = new Object();
private final static Object lockStaticB = new Object();
public void lockStaticMethodA()
{
......
synchronized (lockStaticA)
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " lockMethodA");
try
{
Thread.sleep(100);
}
catch (final InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
......
}
public void lockStaticMethodB()
{
......
synchronized (lockStaticB)
{
for (int i = 0; i < 5; ++i)
{
System.out.println(Thread.currentThread().getName() + " lockStaticMethodB");
try
{
Thread.sleep(100);
}
catch (final InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
......
}
......
}
假设线程T1调用obj#lockStaticMethodA,线程T2调用obj1#lockStaticMethodA,前者将在synchronized(lockStaticA)处阻塞后者。但如果T2调用obj#lockStaticMethodB,将不受影响。
上面应该囊括了synchronized的绝大部分使用场景,不过这么累赘的描述其实可以简单的归类到两种大场景:获取锁的对象是静态还是非静态。如果是非静态,则只有操作同一实例的线程,在执行synchronized的代码区域时,会竞争指定的对象锁。如果是静态,则操作任一实例的线程,在执行synchronized的代码区域时,会竞争指定的对象锁。
最后再强调一下:不同的对象锁之间不受影响。