程无法同时访问该代码块。
synObject可以是this,代表获取当前对象的锁,也可以是类中的一个属性,代表获取该属性的锁。
比如上面的insert方法可以改成以下两种形式:
class InsertData {
? ? private ArrayList arrayList = new ArrayList();
? ?
? ? public void insert(Thread thread){
? ? ? ? synchronized (this) {
? ? ? ? ? ? for(int i=0;i<100;i++){
? ? ? ? ? ? ? ? System.out.println(thread.getName()+"在插入数据"+i);
? ? ? ? ? ? ? ? arrayList.add(i);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
class InsertData {
? ? private ArrayList arrayList = new ArrayList();
? ? private Object object = new Object();
? ?
? ? public void insert(Thread thread){
? ? ? ? synchronized (object) {
? ? ? ? ? ? for(int i=0;i<100;i++){
? ? ? ? ? ? ? ? System.out.println(thread.getName()+"在插入数据"+i);
? ? ? ? ? ? ? ? arrayList.add(i);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
从上面可以看出,synchronized代码块使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步。
另外,每个类也会有一个锁,它可以用来控制对static数据成员的并发访问。
并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。
看下面这段代码就明白了:
public class Test {
? ? public static void main(String[] args)? {
? ? ? ? final InsertData insertData = new InsertData();
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? insertData.insert();
? ? ? ? ? ? }
? ? ? ? }.start();?
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? insertData.insert1();
? ? ? ? ? ? }
? ? ? ? }.start();
? ? }?
}
class InsertData {?
? ? public synchronized void insert(){
? ? ? ? System.out.println("执行insert");
? ? ? ? try {
? ? ? ? ? ? Thread.sleep(5000);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? System.out.println("执行insert完毕");
? ? }
? ?
? ? public synchronized static void insert1() {
? ? ? ? System.out.println("执行insert1");
? ? ? ? System.out.println("执行insert1完毕");
? ? }
}
执行结果;

第一个线程里面执行的是insert方法,不会导致第二个线程执行insert1方法发生阻塞现象。
下面我们看一下synchronized关键字到底做了什么事情,我们来反编译它的字节码看一下,下面这段代码反编译后的字节码为:
public class InsertData {
? ? private Object object = new Object();
? ?
? ? public void insert(Thread thread){
? ? ? ? synchronized (object) {
? ? ? ?
? ? ? ? }
? ? }
? ?
? ? public synchronized void insert1(Thread thread){
? ? ? ?
? ? }
? ?
? ? public void insert2(Thread thread){
? ? ? ?
? ? }
}

从反编译获得的字节码可以看出,synchronized代码块实际上多了monitorenter和monitorexit两条指令。monitorenter指令执行时会让对象的锁计数加1,而monitorexit指令执行时会让对象的锁计数减1,其实这个与操作系统里面的PV操作很像,操作系统里面的PV操作就是用来控制多个线程对临界资源的访问。对于synchronized方法,执行中的线程识别该方法的 method_info 结构是否有 ACC_SYNCHRONIZED 标记设置,然后它自动获取对象的锁,调用方法,最后释放锁。如果有异常发生,线程自动释放锁。
有一点要注意:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。