zoukankan      html  css  js  c++  java
  • [总结] Synchronized汇总

    Java中的每一个对象都可以作为锁。

    • 1对于同步方法,锁是当前实例对象
    • 2对于静态同步方法,锁是当前对象的Class对象
    • 3对于同步方法块,锁是Synchonized括号里配置的对象

    当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁


    我们常引入对象锁和类锁的概念来有助于了解上面的3点论述。

    (1)对象锁(对象实例锁)即Synchronized用于对象实例方法,或者一个对象实例上的锁。

    (2)类锁是用于类的静态方法或者一个类的class对象上的。

    对象锁锁定的是当前实例对象。一个类可以有无数个对象实例,所以一个类可以有无数个对象实例锁。但是每个类只有一个class对象,同一个对象的所有不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁(意味着争夺类锁或者同一个实例的对象锁就会出现同步/锁竞争的情况)(同时注意: olddoor: 类锁和对象锁并不排斥.)

    其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。


    *通过本例子演示类锁/对象锁的使用:
    *1 类锁只是一个虚拟的概念,表示锁定类的静态资源.jvm中的class类可以有多个实例对象,但只有一个class类. 所以每个对象的类锁只有一个,而每个对象可以有任意个实例, 这些实例对象都有自己的对象锁. 对象锁和类锁两种锁互不干扰. (同一个对象实例的对象锁存在竞争情况, 同一个类只有一个类锁所以获取时也存在竞争情况.)

    *2 synchronized的使用:锁要么锁类要么锁实例,静态方法锁的就是锁类,非静态方法锁就是锁实例.
    * synchronized(this)或修饰非静态方法就是锁实例(对象锁)
    * synchronized(this.getClass())/synchronized(类.class)或者synchronized(类的静态变量)或者修饰静态方法.则是锁定类.

    比如classs Entity的方法:

    	public synchronized  void objLock(String who){  //使用synchronized修饰方法
    		for (int i = 0; i < 100; i++) {
    			System.out.println(who + "-" + i);
    		}
    	}

    线程类TestThread13如下:

    package com.j2se.ThreadTest.test001;
    import java.util.Date;
    
    public class TestThread13  extends Thread {
    	private Entity e;
    	public TestThread13(Entity e,String name) {
    		 super(name);
    		 this.e=e;
    	}
    	public TestThread13(String name) {
    		 super(name);
    		 this.e=e;
    	}
    	
    	public void run() {
    		Entity e =new Entity();
    		e.objLock(super.getName());//打印当前线程名字
    	}
    }
    

    写一个执行类的main方法执行内容如下

    	 TestThread13 t133=new TestThread13("133");// 实例锁
    	 TestThread13 t1333=new TestThread13("1333"); // 实例锁
    	     t133.start();
    		 t1333.start();

    执行效果

    1333-0
    1333-1
    1333-2
    133-0
    133-1
    133-2
    133-3
    133-4

    发现交替打印. 因为线程t133和t1333执行run方法调用Entity.objLock时候调用竞争的是不同的对象实例。

    即不同的对象实例锁之间互不干扰(多线程使用不同的对象实例锁),不存在同步竞争情况

    将调用方法改一下

    TestThread13的run方法中的Entity初始化注释
    	public void run() {
    //		Entity e =new Entity();  //注释初始化
    		e.objLock(super.getName());//打印当前线程名字
    	}
    main方法执行线程之前, 先初始化一个Entity并传入线程
    		 Entity e =new Entity(); //初始化唯一的一个实例,用于传入线程
    		 TestThread13 t133=new TestThread13(e,"133");// 
    		 TestThread13 t1333=new TestThread13(e,"1333"); //
    		 t133.start();
    		 t1333.start();
    此时执行效果:
    133-0
    133-1
    133-2
    133-3
    133-4
    133-5
    133-6
    133-7
    133-8
    线程133执行完毕之后才会执行线程1333的方法. 即多线程竞争同一个对象实例锁, 出现锁的竞争.

    现在看看TestThread14,代码和TestThread13很类似.调用entity的不同方法而已.
    public class TestThread14  extends Thread {
    	private Entity e;
    	public TestThread14(Entity e,String name) {
    		 super(name);
    		 this.e=e;
    	}
    	public TestThread14(String name) {
    		 super(name);
    		 this.e=e;
    	}
    	
    	public void run() {
    		Entity e =new Entity();
    		e.classLock(super.getName());//打印当前线程名字
    	}
    }
    entity的.classLock如下,是一个synchronized修饰的静态方法.(需要争夺类锁)
    	public synchronized static void classLock(String who){
    		for (int i = 0; i < 100; i++) {
    			System.out.println(who + "-" + i);
    		}
    	}
    main方法执行如下
    		 TestThread14 t14=new TestThread14("14");// 
    		 TestThread14 t144=new TestThread14("144"); //
    		 t14.start();
    		 t144.start();
    即使在run方法各自初始化了entity (Entity e =new Entity();)
    也发现线程执行完毕一个才会执行一个线程, 因为他们竞争的是类锁. 类锁只有一个, 存在竞争的情况.
    14-0
    14-1
    14-2
    14-3
    14-4
    14-5
    14-6
    ...

    现在将TestThread14的run方法中Entity初始化进行注释
    	public void run() {
    //		Entity e =new Entity();
    		e.classLock(super.getName());//打印当前线程名字
    	}
    main同时执行TestThread14 和TestThread13线程
    		 Entity e=new Entity();
    		 TestThread13 t13=new TestThread13(e,"13");//
    		 TestThread14 t14=new TestThread14(e,"14");// 
    		 t14.start();
    		 t13.start();

    14-0
    13-0
    13-1
    14-1
    13-2
    即使2个线程调用的是同一个类. 因他们争夺的是不同的锁(t13争夺对象锁, t14争夺类锁) 两种类型锁不存在冲突.

    再演示一下Synchronized(this)的问题:
    见Entity的方法snycThis, 用synchronized修饰代码块, 需要竞争this
    	public void snycThis(String who) {
    		synchronized (this) {
    				for (int i = 0; i < 100; i++) {
    					System.out.println(who + "-" + i);
    				}
    		}
    	}
    TestThread15代码如下
    package com.j2se.ThreadTest.test001;
    import java.util.Date;
    
    public class TestThread15  extends Thread {
    	private Entity e;
    	public TestThread15(Entity e,String name) {
    		 super(name);
    		 this.e=e;
    	}
    	public TestThread15(String name) {
    		 super(name);
    		 this.e=e;
    	}
    	
    	public void run() {
    		Entity e =new Entity();
    		e.snycThis(super.getName());
    	}
    }
    main方法
    	 TestThread15 t15=new TestThread15("15");//
    		 TestThread15 t155=new TestThread15("155");// 
    		 t15.start();
    		 t155.start();
    155-0
    15-0
    155-1
    15-1
    155-2
    竞争不同的Entity对象实例锁. 所以不存在冲突.
    如调用同一个对象实例:
     Entity e=new Entity();
    		 TestThread15 t15=new TestThread15(e,"15");//
    		 TestThread15 t155=new TestThread15(e,"155");// 
    		 t15.start();
    		 t155.start();
    	public void run() {
    //		Entity e =new Entity(); //不自己初始化, 使用外部传入的对象实例
    		e.snycThis(super.getName());
    	}
    15-0
    15-1
    15-2
    15-3
    15-4
    15-5
    15-6
    15-7
    15-8
    15-9
    15-10
    15-11
    发现2个线程竞争同一个锁, 当一个线程执行完毕后另外一个线程才会继续执行. 多线程竞争同一个对象实例锁.

    调用
    		 Entity e=new Entity();
    		 TestThread15 t15=new TestThread15(e,"15");//
    		 TestThread13 t13=new TestThread13(e,"13");//
    		 t15.start();
    		 t13.start();
    TestThread15和TestThread13内部的run方法都是竞争实例锁,且不自己初始化对象使用外部main方法也传入同一个对象.
    15-0
    15-1
    15-2
    15-3
    15-4
    15-5
    15-6
    15-7
    15-8
    执行效果:  同一个对象的以下2个方法都是竞争对象锁, 所以存在竞争.
    	public synchronized  void objLock(String who){
    		for (int i = 0; i < 100; i++) {
    			System.out.println(who + "-" + i);
    		}
    	}
    
    	public void snycThis(String who) {
    		synchronized (this) {
    				for (int i = 0; i < 100; i++) {
    					System.out.println(who + "-" + i);
    				}
    		}
    	}

    同样, 过程省略. 我们可以得到以下2个方法都是获得类锁. 多线程情况下他们存在竞争.
    	public void snycClass(String who) {
    		synchronized (Entity.class) {
    				for (int i = 0; i < 100; i++) {
    					System.out.println(who + "-" + i);
    					try {
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    		}
    	}
    
    	public synchronized static void classLock(String who){
    		for (int i = 0; i < 100; i++) {
    			System.out.println(who + "-" + i);
    		}
    	}
    最后: 争夺类变量加锁进行说明:
    public class Entity {
    	public  String name = "entity";
    	private static byte[] lock = new byte[0];
    	private Integer integer=0;
    	private static LinkedList<AlarmVo> volist = new LinkedList<AlarmVo>();
    	
    	private String strLock="1";
    针对Entity的以上集中类型的类变量, 无论是String 还是静态的byte[] 或者Integer  或者static LinkedList
    对如果synchronized锁定的是这些类变量如:
    	public void snycstrLock(String who) {
    		synchronized (strLock) {
    				for (int i = 0; i < 100; i++) {
    					System.out.println(who + "-" + i);
    				}
    		}
    	}
    这里将竞争的既非对象锁 也非所谓的类锁. 而是竞争这里strLock的类型String初始化后所指向的那个堆内存地址. 
    实际上类锁和这里的类变量加锁都是给对象的内存地址加锁. 
    这个类变量的锁和类锁以及对象锁互不冲突. 如有竞争只是针对这个类变量指向的那个对内存地址.
    所以以下例子:
    1. public class ThreadTest_02 extends Thread{
    2. private String lock ;
    3. private String name;
    4. public ThreadTest_02(String name,String lock){
    5. this.name = name;
    6. this.lock = lock;
    7. }
    8. @Override
    9. public void run() {
    10. synchronized (lock) {
    11. for(int i = 0 ; i < 3 ; i++){
    12. System.out.println(name + " run......");
    13. }
    14. }
    15. }
    16. public static void main(String[] args) {
    17. String lock = new String("test");
    18. for(int i = 0 ; i < 5 ; i++){
    19. new ThreadTest_02("ThreadTest_" + i,lock).start(); //已改为1个线程的run方法请求5次
    20. }
    21. }
    22. }

    运行结果:

    ThreadTest_0 run......
    ThreadTest_0 run......
    ThreadTest_0 run......
    ThreadTest_1 run......
    ThreadTest_1 run......
    ThreadTest_1 run......
    ThreadTest_4 run......
    ThreadTest_4 run......
    ThreadTest_4 run......
    ThreadTest_3 run......
    ThreadTest_3 run......
    ThreadTest_3 run......
    ThreadTest_2 run......
    ThreadTest_2 run......
    ThreadTest_2 run......

    在main方法中我们创建了一个String对象lock,并将这个对象赋予每一个ThreadTest2线程对象的私有变量lock我们知道java中存在一个字符串池,那么这些线程的lock私有变量实际上指向的是堆内存中的同一个区域(加锁则需获取堆内存区域的锁),即存放main函数中的lock变量的区域,所以对象锁是唯一且共享的。线程同步!!

    在这里synchronized锁住的就是lock这个String对象。{利用String变量指向的是堆内存这一情况, 每个线程实例调用方法时都获取类变量String lock的锁,以此达到同步的效果}


    综上 是否可以深刻理解了本文第一句 "Java中的每一个对象都可以作为锁。"
    对象锁, 争夺的是对象实例初始化的那个对象. 
    类锁, 争夺的类加载在JVM的那个对象
    类变量, 争夺初始化时的那个地址.
    类和类变量在类加载到JVM编译过程中已经存在. 且只有一个. 而对象锁在线程中初始化的话则有多个. 对同一个对象实例来说对象实例锁也是同一个.
    如果获得类变量的锁定时,你将类变量指向改为null 或者其他对象. 这个所谓的类变量锁也即无法重新找回. 如果没有其他引用, 原来加锁指向的那个地址你知道哪里么.

    针对类变量的应用可以看[效果例子] 分布式事务的简单效果






  • 相关阅读:
    Chapter 4.SQL编程
    Chapter 4. 联合结果集union、插入整个表
    Chapter 4. 聚合函数、字符串函数、类型转换函数、时间日期函数
    Chapter 3. 数据检索(查询)---select、top...order by、distinct、where条件查询、分组查询、模糊查询、null处理
    练习. SQL--学生、教师、分数
    练习. SQL--选修课程练习
    Chapter 3. 数据库约束(SQL语句实现)
    Chapter 3. 数据库约束(设计器操作)
    1月11 数据索引及函数
    1月11日 数据库及表的操作
  • 原文地址:https://www.cnblogs.com/redcoatjk/p/7422823.html
Copyright © 2011-2022 走看看