一、概念

synchronized 是 Java 中的关键字,是利用锁的机制来实现同步的。

锁机制有如下两种特性:

  • 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们也往往称为操作的原子性。

  • 可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。

二、对象锁和类锁

1. 对象锁

在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。

2. 类锁

在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。

三、synchronized 的用法分类

synchronized 的用法可以从两个维度上面分类:

1. 根据修饰对象分类

synchronized 可以修饰方法和代码块

  • 修饰代码块

    • synchronized(this|object) {}

    • synchronized(类.class) {}

  • 修饰方法

    • 修饰非静态方法

    • 修饰静态方法

2. 根据获取的锁分类

  • 获取对象锁

    • synchronized(this|object) {}

    • 修饰非静态方法

  • 获取类锁

    • synchronized(类.class) {}

    • 修饰静态方法,非静态方法

四、synchronized 的用法详解

这里根据获取的锁分类来分析 synchronized 的用法

1、对象锁

这个对象是新建的,跟其他对象没有任何关系:

  /**
* synchronized 修饰非静态方法
*/
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (new SyncThread()) {
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

文章后面所有例子都是采用这四个线程,只是对方法进行修改:

        SyncThread syncThread = new SyncThread();
Thread F_thread1 = new Thread(new SyncThread(), "F_thread1");
Thread F_thread2 = new Thread(new SyncThread(), "F_thread2");
Thread F_thread3 = new Thread(syncThread, "F_thread3");
Thread F_thread4 = new Thread(syncThread, "F_thread4");
F_thread1.start();
F_thread2.start();
F_thread3.start();
F_thread4.start();

运行结果如下:

四个线程同时开始,同时结束,因为作为锁的对象与线程是属于不同的实例。

2、采用类锁,无所谓哪个类,都会被拦截:

   /**
* synchronized 修饰非静态方法
*/
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (MainActivity.class) {
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果如下:

可以发现,采用类锁一次只能通过一个。即使采用的是 MainActivity.class 这个类锁。

3、采用 this 对象锁:

/**
* synchronized 修饰非静态方法
*/
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果如下:

可以发现线程1,2同时结束,3,4有先后,原因是3,4同属于一个实例。

4、synchronized 修饰方法

作用范围是整个方法,所以方法中所有的代码都是同步的:

 /**
* synchronized 修饰非静态方法
*/
private synchronized void sync5() {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

修饰非静态方法:

对于非静态方法,同一个实例的线程访问会被拦截,非同一实例可以同时访问。 即此时是默认对象锁(this)。

修饰静态方法结果

可以看出来,静态方法默认类锁。

总结

1、对于静态方法,由于此时对象还未生成,所以只能采用类锁;

2、只要采用类锁,就会拦截所有线程,只能让一个线程访问。

3、对于对象锁(this),如果是同一个实例,就会按顺序访问,但是如果是不同实例,就可以同时访问。

4、如果对象锁跟访问的对象没有关系,那么就会都同时访问。

参考文章:Java 之 synchronized 详解

synchronized(this) 与synchronized(class) 之间的区别的更多相关文章

  1. Synchronized关键字、Lock,并解释它们之间的区别

    Synchronized 与Lock都是可重入锁,同一个线程再次进入同步代码的时候.可以使用自己已经获取到的锁. Synchronized是悲观锁机制,独占锁.而Locks.ReentrantLock ...

  2. [Android Pro] synchronized与static synchronized 的区别

    reference to :  http://www.cnblogs.com/shipengzhi/articles/2223100.html 1.synchronized与static synchr ...

  3. synchronized与static synchronized 的区别

    synchronized是对类的当前实例加锁,防止其他线程同时访问该类的该实例的synchronized块,这里的概念是“类的当前实例”,而static synchronized是对类的所有实例加锁, ...

  4. 深入理解使用synchronized同步方法和同步代码块的区别

    一.代码块和方法之间的区别 首先需要知道代码块和方法有什么区别: 构造器和方法块,构造器可以重载也就是说明在创建对象时可以按照不同的构造器来创建,那么构造器是属于对象,而代码块呢他是给所有的对象初始化 ...

  5. java中的synchronized同步代码块和同步方法的区别

    下面这两段代码有什么区别? //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized ...

  6. 多线程同步锁和死锁以及synchronized与static synchronized 的区别

    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程.一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序.简而言之:一个程序运行后至少有一个进程,一个进程 ...

  7. Synchronized和Static Synchronized区别

    通过分析这两个用法的分析,我们可以理解Java中锁的概念. 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念), 一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线 ...

  8. synchronized(this)、synchronized(class)与synchronized(Object)的区别

    在多线程开发中,我们经常看到synchronized(this).synchronized(*.class)与synchronized(任意对象)这几种类型同步方法.但是是否知道这几种写法有什么区别了 ...

  9. synchronized和static synchronized的比较

    群里讨论的一个问题,网上别人已经贴出了很详细的说明,这里补充记录下,后面加入个人测试代码. 起因:1月份的时候看群里讨论一道问题,问题内容如下: 一个日本作者-结成浩的<java多线程设计模式& ...

随机推荐

  1. 解决javac和java命令在Mac OSX终端里的乱码问题

    转自:https://www.surfchen.org/archives/710 java和javac在简体中文的Mac OSX的终端(Terminal.app)环境下,默认是以GBK编码的中文输出各 ...

  2. 虚拟机linux挂载光盘显示:mount: you must specify the filesystem type

    虚拟机内 linux 挂载光盘显示:mount: you must specify the filesystem type 今天在虚拟机上挂载镜像文件时提示: 初步断定原因有2: 1.在卸载光盘时使用 ...

  3. RedHat系列软件管理(第二版) --脚本安装

    RedHat系列软件管理 --脚本安装 一.解压缩 tar -zxvf webmin-1.700.tar.gz 二.进入相关目录 cd webmin-1.700 三.如果此时执行./configure ...

  4. 面试之路(8)-BAT面试题之数组和链表的区别

    两种数据结构都是线性表,在排序和查找等算法中都有广泛的应用 各自的特点: 数组: 数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素.但是如果要在数组中增加一个 ...

  5. Access text files using SQL statements by DB Query Analyzer

    Access text files using SQL statements by DB Query Analyzer Ma Gen feng (Guangdong Unitoll Services ...

  6. bash编程语法自我总结

    脚本2种执行方式: 1 直接执行,等于bash衍生一个子程序,当该子程序完成后,子程序内各项变量活动作不会传回父程序 2 利用source执行,直接在父程序中执行 X=/bin/xdo cmd 执行c ...

  7. OS X 平台的 8 个实用终端工具

    本文由 伯乐在线 - shinancao 翻译自 mitchchn.欢迎加入iOS小组.转载请参见文章末尾处的要求. OS X 终端对外开放了许多很强大的UNIX实用工具和脚本.如果你是从Linux转 ...

  8. 容器(list集合)

    --为什么使用集合而不使用数组?why ·集合和数组相似点:都可以存储多个对象,对外作为一个整体存在: ··数组的缺点:1.长度必须在初始化时指定,且固定不变: 2.数组采用连续存储空间,删除和添加元 ...

  9. Python_让人脑阔疼的编码问题(转)+(整理)

    我们要知道python内部使用的是unicode编码,而外部却要面对千奇百怪的各种编码,比如作为中国程序经常要面对的gbk,gb2312,utf8等,那这些编码是怎么转换成内部的unicode呢? 首 ...

  10. Linux下使用Kickstart自动化安装平台架构

    PXE工作于Client/Server的网络模式.在启动过程中,终端要求服务器分配IP地址,再用TFTP协议下载一个自动启动软件包到内存中执行. 要使用kickstart安装平台,包括完整的架构为:K ...