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. 编写一个求和函数sum,使输入sum(2)(3)或输入sum(2,3),输出结果都为5

    昨天的笔试题,做的一塌糊涂,题目考的都很基础而且很细,手写代码对我来说是硬伤啊.其中有一道是这个,然而看到题目的时候,根本没有想到arguments:然后现在就恶补一下. arguments:用在函数 ...

  2. 自学python之路(day6)

    一 函数的定义与调用 现在需要一个程序来实现len()的功能. 计算字符串 s 长度 s='好好学习' #函数的定义def my_len(): i = for k in s: i += print(i ...

  3. L360 Most People Spend Their Time in Just 25 Places

    Some people are always out on the town, going to concerts, restaurant openings, you name it. They're ...

  4. .NET 并行计算和并发10-lock锁

    class Program { private static List<int> intlist; static void Main(string[] args) { intlist = ...

  5. js图片库

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. who are we?

    human been like animals,we work for our master. when we dreaming,we drive the machine run.

  7. mac安装brew简单方法

    安装方法:命令行输入 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ma ...

  8. HBase原理和设计

    转载 2016年1月10日:http://www.sysdb.cn/index.php/2016/01/10/hbase_principle/ 简介 架构 数据组织 原理 RS定位 region写入 ...

  9. spring+mybatis的多源数据库配置实战

    前言: 关于spring+mybatis的多源数据库配置, 其实是个老生常谈的事情. 网上的方案出奇的一致, 都是借助AbstractRoutingDataSource进行动态数据源的切换. 这边再无 ...

  10. SpringSecurity身份验证基础入门

    对于没有访问权限的用户需要转到登录表单页面.要实现访问控制的方法多种多样,可以通过Aop.拦截器实现,也可以通过框架实现(如:Apache Shiro.Spring Security). pom.xm ...