• 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

  • 相关阅读:
    佛教-著作:《般若泼若密心经》
    学科-几何:分形几何学
    战国-散文:《生于忧患,死于安乐》
    北宋-词:《临江仙·送王缄》
    音乐:《河西走廊之梦》
    影视-纪录片:《河西走廊》
    汉语-汉字:黾
    动物-昆虫:水螳螂
    动物-昆虫:水黾
    主程的晋升攻略(3):IP、DNS和CDN
  • 原文地址:https://www.cnblogs.com/bydream/p/7511798.html
Copyright © 2020-2023  润新知