Synchronized和Static Synchronized区别

通过分析这两个用法的分析,我们可以理解java中锁的概念。一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁)。实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。下面的文章做了很好的总结:

1.synchronized与static synchronized 的区别

synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”, 类的两个不同实例就没有这种约束了。那么static synchronized恰好就是要控制类的所有实例的访问了,static synchronized是限制线程同时访问jvm中该类的所有实例同时访问对应的代码快。实际上,在类中某方法或某代码块中有 synchronized,那么在生成一个该类实例后,改类也就有一个监视快,放置线程并发访问改实例synchronized保护快,而static synchronized则是所有该类的实例公用一个监视快了,也也就是两个的区别了,也就是synchronized相当于 this.synchronized,而static synchronized相当于Something.synchronized.

一个日本作者-结成浩的《java多线程设计模式》有这样的一个列子:

pulbic class Something(){
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

那么,假如有Something类的两个实例x与y,那么下列哪几组方法何以被1个以上线程同时访问呢

a. x.isSyncA()与x.isSyncB() 
b. x.isSyncA()与y.isSyncA()
c. x.cSyncA()与y.cSyncB()
d. x.isSyncA()与Something.cSyncA()

这里,很清楚的可以判断:

a情况,都是对同一个实例的synchronized域访问,因此不能被同时访问
b情况,是针对不同实例的,因此可以同时被访问
c情况,因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.isSyncA()与 Something.isSyncB()了,因此不能被同时访问。
那么,第d情况呢?,书上的 答案是可以被同时访问的,答案理由是synchronzied的是实例方法与synchronzied的类方法由于锁定(lock)不同的原因。

个人分析也就是synchronized 与static synchronized 相当于两帮派,各自管各自,相互之间就无约束了,可以被同时访问。目前还不是分清楚java内部设计synchronzied是怎么样实现的。
实例:

public class TestSynchronized   
{    
    public synchronized void test1()   
    {    
              int i = 5;    
              while( i-- > 0)   
              {    
                   System.out.println(Thread.currentThread().getName() + " : " + i);    
                   try   
                   {    
                        Thread.sleep(500);    
                   }   
                   catch (InterruptedException ie)   
                   {    
                   }    
              }    
    }    
      
    public static synchronized void test2()   
    {    
         int i = 5;    
         while( i-- > 0)   
         {    
              System.out.println(Thread.currentThread().getName() + " : " + i);    
              try   
              {    
                   Thread.sleep(500);    
              }   
              catch (InterruptedException ie)   
              {    
              }    
         }    
    }    
      
    public static void main(String[] args)   
    {    
         final TestSynchronized myt2 = new TestSynchronized();    
         Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, "test1"  );    
         Thread test2 = new Thread(  new Runnable() {  public void run() { TestSynchronized.test2();   }  }, "test2"  );    
         test1.start();    
         test2.start();    
//         TestRunnable tr=new TestRunnable();  
//         Thread test3=new Thread(tr);  
//         test3.start();  
    }   
    
}  

运行结果:

test1 : 4  
test2 : 4  
test1 : 3  
test2 : 3  
test2 : 2  
test1 : 2  
test2 : 1  
test1 : 1  
test1 : 0  
test2 : 0  

上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

结论:A: synchronized static是某个类的范围,synchronized static cSync{}防止多个线程同时访问这个 类中的synchronized static 方法。它可以对类的所有对象实例起作用。

B: synchronized 是某实例的范围,synchronized isSync(){}防止多个线程同时访问这个实例中的synchronized 方法。

其实总结起来很简单。

一个锁的是类对象,一个锁的是实例对象。
若类对象被lock,则类对象的所有同步方法全被lock;
若实例对象被lock,则该实例对象的所有同步方法全被lock。

2.synchronized方法与synchronized代码快的区别

synchronized methods(){} 与synchronized(this){}之间没有什么区别,只是synchronized methods(){} 便于阅读理解,而synchronized(this){}可以更精确的控制冲突限制访问区域,有时候表现更高效率。

3.synchronized关键字是不能继承的

这个在http://www.learndiary.com/archives/diaries/2910.htm一文中看到的,我想这一点也是很值得注意的,继承时子类的覆盖方法必须显示定义成synchronized。(但是如果使用继承开发环境的话,会默认加上synchronized关键字)

两种方式效率比较:

1、同步块,代码如下:

package com.bjtest.belen;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class TestSynchronized {
 
		/**
		 * @param args
		 */
		public static void main(String[] args) {
			
			ExecutorService service = Executors.newCachedThreadPool();
			final CountDownLatch cdOrder = new CountDownLatch(1);
			final CountDownLatch cdAnswer = new CountDownLatch(3);
			
			final SynchonizedClass sc = new SynchonizedClass();
			for(int i=0; i<3; i++){
				Runnable runnable = new Runnable(){
 
					public void run() {
						try{
							cdOrder.await();
							sc.start();
							cdAnswer.countDown();
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					
				};
				service.execute(runnable);
			}
			try{
				Thread.sleep((long) (Math.random()*10000));
				System.out.println("线程" + Thread.currentThread().getName() + 
						"发布执行命令");
				cdOrder.countDown();
				long beginTime = System.currentTimeMillis();
				System.out.println("线程" + Thread.currentThread().getName() + 
				"已经发送命令,正在等待结果");
				cdAnswer.await();
				System.out.println("线程" + Thread.currentThread().getName() + 
				"已收到所有响应结果,所用时间为:" + (System.currentTimeMillis()-beginTime));
			}catch(Exception e){
				e.printStackTrace();
			}
			service.shutdown();
	}
}
 
class SynchonizedClass{
	
	public void start() throws InterruptedException{
		
		Thread.sleep(100);//执行其它逻辑消耗时间
		synchronized(this){
		 System.out.println("我运行使用了 10 ms");
		}
	}
}

运行结果如下:

线程main发布执行命令
线程main已经发送命令,正在等待结果
我运行使用了 10 ms
我运行使用了 10 ms
我运行使用了 10 ms
线程main已收到所有响应结果,所用时间为:110

同步方法,代码如下:

package com.bjtest.belen;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class TestSynchronized {
 
		/**
		 * @param args
		 */
		public static void main(String[] args) {
			
			ExecutorService service = Executors.newCachedThreadPool();
			final CountDownLatch cdOrder = new CountDownLatch(1);
			final CountDownLatch cdAnswer = new CountDownLatch(3);
			
			final SynchonizedClass sc = new SynchonizedClass();
			for(int i=0; i<3; i++){
				Runnable runnable = new Runnable(){
 
					public void run() {
						try{
							cdOrder.await();
							sc.start();
							cdAnswer.countDown();
						}catch(Exception e){
							e.printStackTrace();
						}
					}
					
				};
				service.execute(runnable);
			}
			try{
				Thread.sleep((long) (Math.random()*10000));
				System.out.println("线程" + Thread.currentThread().getName() + 
						"发布执行命令");
				cdOrder.countDown();
				long beginTime = System.currentTimeMillis();
				System.out.println("线程" + Thread.currentThread().getName() + 
				"已经发送命令,正在等待结果");
				cdAnswer.await();
				System.out.println("线程" + Thread.currentThread().getName() + 
				"已收到所有响应结果,所用时间为:" + (System.currentTimeMillis()-beginTime));
			}catch(Exception e){
				e.printStackTrace();
			}
			service.shutdown();
	}
}
 
class SynchonizedClass{
	
	public synchronized void start() throws InterruptedException{
		
		Thread.sleep(100);//执行其它逻辑消耗时间
//		synchronized(this){
		 System.out.println("我运行使用了 10 ms");
//		}
	}
}

运行结果如下:

线程main发布执行命令
线程main已经发送命令,正在等待结果
我运行使用了 10 ms
我运行使用了 10 ms
我运行使用了 10 ms
线程main已收到所有响应结果,所用时间为:332

两者相差:222ms。

下面我们着重介绍java中的 Sychronized的用法,具体为:同步方法 与 同步块

synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:

 

public synchronized void accessVal(int newVal);

  synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

  在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为static synchronized ,以控制其对类的静态成员变量的访问。

  synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:

  

synchronized(syncObject) {

  //允许访问控制的代码

  }

   synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

注意:
在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合逻辑。
同样,在同步程序块内调用yeild方法让出CPU资源也没有意义,因为你占用着锁,其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。

总结:

基础测试类:

package com.quant.dev.modules.dev.enetity;
    /**
     * @program: dev
     * @description:
     * @author: Mr.EternityZhang
     * @create: 2019-06-24 17:07
     */
    public class TestSync {
        //代码块
        public void test1()
        {
            synchronized(this)
            {
                int i = 5;
                while( i-- > 0)
                {
                    System.out.println(Thread.currentThread().getName() + " : " + i);
                    try
                    {
                        Thread.sleep(500);
                    }
                    catch (InterruptedException ie)
                    {
                    }
                }
            }
        }
        //同步方法
        public synchronized void test2()
        {
            int i = 5;
            while( i-- > 0)
            {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try
                {
                    Thread.sleep(500);
                }
                catch (InterruptedException ie)
                {
                }
            }
        }
        //同步方法
        public  synchronized void test3()
        {
            int i = 5;
            while( i-- > 0)
            {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try
                {
                    Thread.sleep(500);
                }
                catch (InterruptedException ie)
                {
                }
            }
        }
        //同步静态方法
        public static synchronized void test4()
        {
            int i = 5;
            while( i-- > 0)
            {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try
                {
                    Thread.sleep(500);
                }
                catch (InterruptedException ie)
                {
                }
            }
        }
        //普通方法
        public void test5()
        {
            int i = 5;
            while( i-- > 0)
            {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try
                {
                    Thread.sleep(500);
                }
                catch (InterruptedException ie)
                {
                }
            }
        }
    
    }

1、synchronized关键字的作用域有二种:



    1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
    
    public static void main(String[] args)
        {
            final TestSync myt1 = new TestSync();
            Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test2();  }  }, "test1"  );
            Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test3();   }  }, "test2"  );
            test1.start();
            test2.start();
    
        }
    执行结果:
    test1 : 4
    test1 : 3
    test1 : 2
    test1 : 1
    test1 : 0
    test2 : 4
    test2 : 3
    test2 : 2
    test2 : 1
    test2 : 0
    印证:前提同一个对象,如果一个线程获取了锁,另外一个线程无法访问其他的synchronized方法,直至锁释放
    
    改变main方法:
    public static void main(String[] args)
    {
        final TestSync myt1 = new TestSync();
        final TestSync myt2 = new TestSync();
        Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test2();  }  }, "test1"  );
        Thread test2 = new Thread(  new Runnable() {  public void run() { myt2.test3();   }  }, "test2"  );
        test1.start();
        test2.start();

    }
    执行结果:
    test2 : 4
    test1 : 4
    test1 : 3
    test2 : 3
    test2 : 2
    test1 : 2
    test2 : 1
    test1 : 1
    test2 : 0
    test1 : 0
    印证:其它线程照样可以同时访问相同类的 另一个对象实例 中的synchronized方法;同时证明了synchronized是非公平锁,先开始的线程不一定先执行
    

    2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程中不同的实例对象(或者同一个实例对象)同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
    public static void main(String[] args)
    {
        final TestSync myt1 = new TestSync();
        final TestSync myt2 = new TestSync();
        Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test4();  }  }, "test1"  );
        Thread test2 = new Thread(  new Runnable() {  public void run() { myt2.test4();   }  }, "test2"  );
        test1.start();
        test2.start();

    }
    执行结果:
    test1 : 4
    test1 : 3
    test1 : 2
    test1 : 1
    test1 : 0
    test2 : 4
    test2 : 3
    test2 : 2
    test2 : 1
    test2 : 0
    印证:不同实例,访问static synchronized方法,锁起作用,产生阻塞.

2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/}(或者synchronized(obj){/区块/}),它的作用域是当前对象;

3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法

对synchronized(this)的一些理解(很好的解释了对象锁,注意其中的this关键字)

一、(前提同一实例对象)当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块.同时,一个线程访问synchronized(this)同步代码块,另外一个线程不能访问synchronized(this)同步代码块,也不能访问synchronized方法,直至锁释放。

public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test1();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0


public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test2();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0

二、(前提:同一实例对象或者不同实例都可以)然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块或者非synchronized方法.

public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test5();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test2 : 4
test1 : 4
test1 : 3
test2 : 3
test1 : 2
test2 : 2
test1 : 1
test2 : 1
test2 : 0
test1 : 0

三、(前提同一实例对象)尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞,synchronized方法也被阻塞。

public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test1();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0


public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test2();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0

四、(前提同一实例对象)第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。注意:static synchronized方法不受影响,仍然可以执行

public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test2();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0


public static void main(String[] args)
{
    final TestSync myt1 = new TestSync();
    final TestSync myt2 = new TestSync();
    Thread test1 = new Thread(  new Runnable() {  public void run() {  myt1.test1();  }  }, "test1"  );
    Thread test2 = new Thread(  new Runnable() {  public void run() { myt1.test4();   }  }, "test2"  );
    test1.start();
    test2.start();

}
执行结果:
test1 : 4
test2 : 4
test2 : 3
test1 : 3
test2 : 2
test1 : 2
test2 : 1
test1 : 1
test2 : 0
test1 : 0

五、synchronized修饰方法和用synchronized(this)获取的是该对象的锁,两者效果一样

六、synchronized(非this对象)同步代码块

好处: 如果在一个类中有很多个synchronized方法,这是虽然可以实现同步,但是会受到阻塞。影响效率。但是如果使用了同步代码块的非this锁,则该synchronized(非this)代码块和程序中的synchronized同步方法是异步的。不和其他线程争取this锁,可以提高运行效率。
使用synchronized(任意自定义对象)进行同步操作,对象监视器必须是同一个对象。如果不是同一个,运行就是异步执行了。

七、synchronized (类.class)

一个类只有一个类对象,其实类锁也是类的对象锁,静态的方法或代码块是属于类(类对象)的,所以synchronized修饰静态资源需要获取类锁。
static修饰的静态方法 和 static修饰静态方法内有synchronized代码块获取的也是类锁
synchronized (类.class)修饰非静态代码块,所有类的实例包括类对象本身(类.class)都会被堵塞。

终极总结(已验证)

以单实例还是多实例区分

同一实例前提下:

synchronized(this)同步代码块/synchronized方法/static synchronized方法执行的时候,普通方法都可以执行,不存在互斥行为;
synchronized(this)同步代码块/synchronized方法存在互斥;
synchronized(this)同步代码块/synchronized(this)同步代码块存在互斥;
synchronized(this)同步代码块/static synchronized方法不存在互斥;
synchronized方法/static synchronized方法不存在互斥;
synchronized方法/synchronized方法存在互斥;
static synchronized方法与static synchronized方法存在互斥;
synchronized(***.class)与static synchronized方法效果一样,锁类
synchronized修饰方法和用synchronized(this)获取的是该对象的锁

不同实例:

实例a的synchronized(this)同步代码块/synchronized方法/static synchronized方法执行的时候,与实例b的普通方法都可以执行,不存在互斥行为;
实例a的synchronized(this)同步代码块与实例b的synchronized方法不存在互斥;
实例a的synchronized(this)同步代码块/实例b的synchronized(this)同步代码块不存在互斥;
实例a的synchronized(this)同步代码块与实例b的static synchronized方法不存在互斥;
实例a的synchronized方法与实例b的static synchronized方法不存在互斥;
实例a的synchronized方法与实例b的synchronized方法不存在互斥;
实例a的static synchronized方法与实例b的static synchronized方法存在互斥;
synchronized(***.class)与static synchronized方法效果一样,锁类
synchronized修饰方法和用synchronized(this)获取的是该对象的锁

以synchronized方法(synchronized(this)与synchronized方法相同)与static synchronized方法区分

synchronized方法与synchronized(this)主要针对方法起作用,单实例互斥,多实例不互斥
static synchronized方法针对类起作用,无论单实例还是多实例都互斥
synchronized(***.class)与static synchronized方法效果一样,锁类
synchronized修饰方法和用synchronized(this)获取的是该对象的锁

别人的总结,可参考

1、synchronized 关键字主要用来解决多线程并发同步问题,可以用来修饰类的实例方法、静态方法、代码块;

2、synchronized 实例方法实际保护的是同一个对象的方法调用,当为不同对象时多线程是可以同时访问同一个 synchronized 方法的;

3、synchronized 静态方法和 synchronized 实例方法保护的是不同对象,不同的两个线程可以同时执行 synchronized 静态方法,另一个执行 synchronized 实例方法,因为 synchronized 静态方法保护的是 class 类对象,synchronized 实例方法保护的是 this 实例对象;

4、synchronized 代码块同步的可以是任何对象,因为任何对象都有一个锁和等待队列。

5、synchronized 具备可重入性,对同一个线程在获得锁之后在调用其他需要同样锁的代码时可以直接调用,其可重入性是通过记录锁的持有线程和持有数量来实现的,调用 synchronized 代码时检查对象是否已经被锁,是则检查是否被当前线程锁定,是则计数加一,不是则加入等待队列,释放时计数减一直到为零释放锁。

6、synchronized 还具备内存可见性,除了实现原子操作避免竞态以外对于明显是原子操作的方法(譬如一个 boolean 状态变量 state 的 get 和 set 方法)也可以通过 synchronized 来保证并发的可见性,在释放锁时所有写入都会写回内存,而获得锁后都会从内存读取最新数据;不过对于已经是原子性的操作为了保证内存可见性而使用 synchronized 的成本会比较高,轻量级的选择应该是使用 volatile 修饰,一旦修饰 java 就会在操作对应变量时插入特殊指令保证可见性。

7、synchronized 是重量级锁,其语义底层是通过一个 monitor 监视器对象来完成,其实 wait、notify 等方法也依赖于 monitor 对象,所以这就是为什么只有在同步的块或者方法中才能调用 wait、notify 等方法,否则会抛出 IllegalMonitorStateException 异常的原因,监视器锁(monitor)的本质依赖于底层操作系统的互斥锁(Mutex Lock)实现,而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,所以这就是为什么 synchronized 效率低且重量级的原因(Java 1.6 进行了优化,但是相比其他锁机制还是略显偏重)。

8、synchronized 在发生异常时会自动释放线程占用的锁资源,Lock 需要在异常时主动释放,synchronized 在锁等待状态下无法响应中断而 Lock 可以。
--------------------- 
原文:https://blog.csdn.net/u010647035/article/details/82320571 

参考

原文:https://blog.csdn.net/liovey/article/details/7456096

posted @ 2020-01-28 20:17  未月廿三  阅读(2466)  评论(0编辑  收藏  举报