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 或者其他对象. 这个所谓的类变量锁也即无法重新找回. 如果没有其他引用, 原来加锁指向的那个地址你知道哪里么.

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


[总结] Synchronized汇总的更多相关文章

  1. Synchronized的实现原理(汇总)

      一.Java中synchronized关键字的作用 总所周知,在并发环境中多个线程对同一个资源进行访问很可能出现脏读等一系列线程安全问题.这时我们可以用加锁的方式对访问共享资源的代码块进行加锁,以 ...

  2. [CareerCup] 16.6 Synchronized Method 同步方法

    16.6 You are given a class with synchronized method A and a normal method B. If you have two threads ...

  3. 史上最全的 Java 新手问题汇总

    史上最全的 Java 新手问题汇总   Java是目前最流行的编程语言之一——它可以用来编写Windows程序或者是Web应用,移动应用,网络程序,消费电子产品,机顶盒设备,它无处不在. 有超过30亿 ...

  4. 【转】Java方向如何准备BAT技术面试答案(汇总版)

    原文地址:http://www.jianshu.com/p/1f1d3193d9e3 这个主题的内容之前分三个篇幅分享过,导致网络上传播的比较分散,所以本篇做了一个汇总,同时对部分内容及答案做了修改, ...

  5. java面试笔试大汇总

    java面试笔试题大汇总5 JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象:2.继承:3.封装:4. 多态性: 2.String是最基本的数据类型吗? 基本数据类型包括byte.int. ...

  6. (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别

    场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...

  7. Java面试系列之HashMap大扫盲汇总

    PS:整理的稍微有点急,不足之处,望各路道友指正,List相关可以查看前一篇随笔! HashMap的工作原理是近年来常见的Java面试题,几乎每个Java程序员都知道HashMap,都知道哪里要用Ha ...

  8. 线程池 队列 synchronized

    线程池 BlockingQueue synchronized volatile 本章从线程池到阻塞队列BlockingQueue.从BlockingQueue到synchronized 和 volat ...

  9. 并发的核心:CAS 与synchronized, Java8是如何优化 CAS 的?

    大家可能都听说说 Java 中的并发包,如果想要读懂 Java 中的并发包,其核心就是要先读懂 CAS 机制,因为 CAS 可以说是并发包的底层实现原理. 今天就带大家读懂 CAS 是如何保证操作的原 ...

随机推荐

  1. elasticsearch(6) 映射和分析

    类似关系型数据库中每个字段都有对应的数据类型,例如nvarchar.int.date等等,elasticsearch也会将文档中的字段映射成对应的数据类型,这一映射可以使ES自动生成的,也是可以由我们 ...

  2. 2018-2019-2 《网络对抗技术》Exp5 MSF基础应用 20165326

    Exp5 MSF基础应用 实践内容 主动攻击实践 ms17_010_enternalblue 靶机:win7 x64成功 针对浏览器的攻击 ms14_064_ole_code_execution 靶机 ...

  3. 2018-2019-2 网络对抗技术 20165326 Exp3 免杀原理与实践

    免杀原理与实践 目录 知识点问答 实践内容 遇到的问题 心得体会 知识点 meterpreter免杀 基础问题回答 杀软是如何检测出恶意代码的? 特征码(基于签名):模式匹配,比对特征码库 启发式:通 ...

  4. Windows Server 2008系统

    Windows Server 2008特点 1,可操作性 2,可管理性 3,可扩展性 4,可用性 5,安全性 Windows Server 2008提供两个最常用默认用户账户Administrator ...

  5. AIX下core文件的分析

    笔者曾在AIX系统下使用C语言开发多个应用系统.众所周知,C语言编写程序时容易出现内存使用不当的BUG,例如内存越界.使用野指针.内存未初始化等等.在程序运行时,这些BUG很可能造成程序崩溃,但在测试 ...

  6. MySQL中查询、删除重复记录的方法大全

    查找所有重复标题的记录: select title,count(*) as count from user_table group by title having count>1; SELECT ...

  7. os模块(操作系统)

    os.getcwd() #获取当前py工作路径 os.chdir("路径") #改变当前路径到指定路径 os.curdir #返回当前工作路径 os.pardir #返回当前路径上 ...

  8. Python帮助

    我们可以很容易的通过Python解释器获取帮助.如果想知道一个对象(object)更多的信息,那么可以调用help(object)!另外还有一些有用的方法,dir(object)会显示该对象的大部分相 ...

  9. MT4 取K线

    目标:动态获取最新K线并通过DLL发送出去,symbols和periods可配置. //+------------------------------------------------------- ...

  10. 常见Soc平台图形内存管理学习笔记

    硬件编解码.硬件图像scale等过程,是在专有的硬件单元里进行,其使用的内存也是专有的内存,这种内存多是SoC中图形内存.如此方便与硬件加速图形渲染.图像显示.硬件图像加速处理等功能相交互. 上述过程 ...