Java中synchronized关键字理解

好记性不如烂笔头~~

并发编程中synchronized关键字的地位很重要,很多人都称它为重量级锁。利用synchronized实现同步的基础:Java中每一个对象都可以作为锁。具体表现为以下三种形式。

(1)对于普通同步方法,锁是当前实例对象。

(2)对于静态同步方法,锁是当前类的Class对象。

(3)对于同步方法块,锁是synchronized括号里配置的对象。

一、普通同步方法

使用synchronized关键字修饰一个普通方法,锁住的是当前实例的对象。当synchronized锁住该对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。

实验一

class Synch{
	public synchronized  void test1(){
		System.out.println("test1开始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test1结束");
	}
	public synchronized void test2(){
		System.out.println("test2开始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test2结束");
	}
}

public class SyncTest extends Synch{
	
   public static void main(String args[]){
	   Synch s=new Synch();
	   Thread t1=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s.test1();
		}
		   
	   });
	   Thread t2=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s.test2();
		}});
	   t1.start();
	   t2.start();
   }
}

  实验结果:

分析上述代码,类Synch中有两个普通同步方法test1和test2.在主函数中实现了该类,同时定义两个thread线程,run方法中分别调用类Synch的方法。synchronized实现的普通同步方法,锁住的是当前实例对象,即Synch类对象。由于两个线程是调用的同一个对象中的同步方法,所以只有一个线程释放该对象的锁,另一个线程才能调用。

实验二

class Sync{
	public synchronized void test(String threadname){
		
		System.out.println(threadname+"开始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(threadname+"结束");

	}
}
class MyThread extends Thread{
	public int i;
	public MyThread(int i){
		this.i=i;
	}
	Sync s=new Sync();
	public void run(){		
		//Sync sync=new Sync();
		s.test("Thread"+i);
	}
}
public class SynTest {
    public static void main(String args[]){
    	for(int i=0;i<3;i++){
			Thread thread=new MyThread(i);
			thread.start();
		}
    }
}

 实验结果:

上述代码,每个线程中都new了一个Sync类的对象,也就是产生了三个Sync对象,由于不是同一个对象,所以可以多线程同时运行synchronized方法。

二、静态同步方法

对于静态同步方法,锁是当前类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。只有一个线程结束后,另一个线程才能获得锁。

实验三

class Synch{
	public static synchronized  void test1(){
		System.out.println("test1开始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test1结束");
	}
	public static synchronized  void test2(){
		System.out.println("test2开始");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("test2结束");
	}
}

public class SyncTest extends Synch{
	
   public static void main(String args[]){
	   Synch s1=new Synch();
	   Synch s2=new Synch();
	   Thread t1=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s1.test1();
		}
		   
	   });
	   Thread t2=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			s2.test2();
		}});
	   t1.start();
	   t2.start();
   }
}

  实验结果:

三、同步方法块

这部分更好理解,锁住的是synchronized括号里配置的对象。

实验四

public class Threadtest implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized(this){
			  for (int i = 0; i < 5; i++) {  
                  System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);  
             }  
		}
	}
	public static void main(String[] args) {  
        Threadtest t1 = new Threadtest();  
        Thread ta = new Thread(t1, "A");  
        Thread tb = new Thread(t1, "B");  
        ta.start();  
        tb.start();  
   } 
}

运行结果:

上述代码,synchronized代码块括号里配置的对象是this,同一个对象,所以只有一个线程访问该代码块结束后,释放锁,另一个线程才能访问该代码块。

需要注意的是,其他线程可以访问非synchronized(this)同步代码。如下代码

实验五

class MyThread1{
	public void test1(){
    	synchronized(this){
    		System.out.println("同步代码块-test1开始");
    		try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    		System.out.println("同步代码块-test1结束");
    	}
    }
    public void test2(){
    	try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	System.out.println("非同步代码块-test2");
    }
}
public class ThreadTest1 {
	public static void main(String args[]){
    MyThread1 t=new MyThread1();
    Thread t1=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			t.test1();
		}
    	
    });
    Thread t2=new Thread(new Runnable(){

		@Override
		public void run() {
			// TODO Auto-generated method stub
			t.test2();
			
		}
    	
    });
    t1.start();
    t2.start();
	}
}

  运行结果:

synchronized括号里配置的是this,只是对这一段代码进行了加锁,同一个对象,只有一个线程能访问该代码块,释放锁之后,其他线程才可以访问,但是并不影响其他线程访问非同步代码块。

同步代码块,同步方法的实现~~简单理解

对于同步代码块是使用monitorenter、monitorexit指令实现的。每个对象都有一个监视器锁(monitor),当monitor被占用时,就会处于锁定状态。

线程执行monitorenter指令尝试获取monitor的所有权。

  • 如果monitor的进入数为0,则该线程进入monitor,并将进入数设置为1
  • 如果该线程已经占有了该monitor,重新进入,进入数也要+1
  • 如果其他线程占用了monitor,则该线程进入阻塞状态,知道进入数为0

对于同步方法,常量池中多了ACC_SYNCHRONIZED标识符,方法调用时,先检查该标识符的访问标志,如果设置了,则进程先获取monitor,获取成功后才能执行方法体,方法执行完之后再释放monitor。

整理下,要不会忘记~~参考http://www.cnblogs.com/QQParadise/articles/5059824.html

原文地址:https://www.cnblogs.com/bydream/p/7511798.html