概述:
synchronized是java用于处理多线程同步的一个关键字,用于标记一个方法/代码块,使之成为同步方法/同步块。
用synchronized可以避免多线程处理时的竞态条件问题。
相关概念:
在java中,所有对象都有一个锁(也叫对象监视器/内置锁),并且JVM会记录对象的加锁次数。
内置锁的可重入性:
当一个线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将该锁的计数值置为1。如果同一个线程再次获取这个锁,计数值将递增,而当线程退出同步代码块时,计数值会相应递减。当计数值为0时,表示锁已被释放。
重入性简单的说就是同一个线程可以连续持有一个内置锁。
当一个线程持有某对象的锁之后,其他线程只有等锁被释放之后才能持有该锁,并执行相关代码块。线程在同步代码执行完成或者出异常后将自动释放锁。
用法:
synchronized可以用来标记4种类型的代码:
- 非静态方法
- 静态方法
- 非静态方法内的同步块
- 静态方法内的同步块
下面分别说明这四中类型的写法:
一. 非静态同步方法:
public synchronized void method( int value )
{
}
synchronized 关键字用在非静态方法上,表示同步锁是在对象级别,对象锁说明2个线程调用同一个实例对象的同步方法才会产生锁竞争的情况,2个线程分别调用同一个类的2个不同实例的同步方法是互不影响的。
注意:多个线程调用同一个实例对象的同步方法,这里的同步方法不一定是同一个方法,即使是2个不同的方法,只要2个方法都用synchronized 标记,那么在多线程情况下就会产生互斥。因为锁实际是在实例对象上。
下面我们动手来试试synchronized 用在非静态方法上的效果:
public class TestSynchronized {
public static void main(String[] args) {
NumberSpeaker numberSpeaker1 = new NumberSpeaker("numberSpeaker1");
NumberSpeaker numberSpeaker2 = new NumberSpeaker("numberSpeaker2");
Thread thread1 = new NumberSpeakerThread(numberSpeaker1 );
Thread thread2 = new NumberSpeakerThread(numberSpeaker2 );
thread1.start();
thread2.start();
}
}
class NumberSpeaker{
public String speakerName ;
public NumberSpeaker(String speakerName ) {
this.speakerName = speakerName ;
}
public synchronized void speak(int number) {
for(int i = 0; i < number ; i ++) {
try {
Thread.sleep(500);
} catch (InterruptedException e ) {
e.printStackTrace();
}
System. out.println(this .speakerName + " speaking: " + i );
}
}
}
class NumberSpeakerThread extends Thread {
private NumberSpeaker numberSpeaker = null;
public NumberSpeakerThread(NumberSpeaker numberSpeaker ) {
this.numberSpeaker = numberSpeaker ;
}
@Override
public void run() {
this.numberSpeaker.speak(10);
}
}
我们可以用这段代码做细微调整来模拟3种情形:
1. 2个线程调用同一个对象的非同步方法,代码修改如下:
Thread thread1 = new NumberSpeakerThread(numberSpeaker1 );
Thread thread2 = new NumberSpeakerThread(numberSpeaker1 );
// 改为numberSpeaker1
public void speak( int number)
// 把synchronized关键字去掉
执行代码,结果是2个线程并发执行:
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 1
numberSpeaker1 speaking: 1
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 3
numberSpeaker1 speaking: 3
......
2. 2个线程调用同一个对象的同步方法,代码修改如下:
Thread thread1 = new NumberSpeakerThread(numberSpeaker1 );
Thread thread2 = new NumberSpeakerThread(numberSpeaker1 );
// 改为numberSpeaker1
public synchronized void speak( int number)
// 把synchronized关键字加上
执行代码,结果是2个线程按顺序执行:
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 1
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 3
numberSpeaker1 speaking: 4
numberSpeaker1 speaking: 5
numberSpeaker1 speaking: 6
numberSpeaker1 speaking: 7
numberSpeaker1 speaking: 8
numberSpeaker1 speaking: 9
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 1
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 3
numberSpeaker1 speaking: 4
numberSpeaker1 speaking: 5
......
3. 2个线程调用同2个对象的同步方法,代码修改如下:
Thread thread1 = new NumberSpeakerThread(numberSpeaker1 );
Thread thread2 = new NumberSpeakerThread(numberSpeaker2 );
// 改为numberSpeaker2
public synchronized void speak( int number)
// 把synchronized关键字加上
执行代码,结果是2个线程并发执行:
numberSpeaker2 speaking: 0
numberSpeaker1 speaking: 0
numberSpeaker2 speaking: 1
numberSpeaker1 speaking: 1
numberSpeaker2 speaking: 2
numberSpeaker1 speaking: 2
numberSpeaker2 speaking: 3
numberSpeaker1 speaking: 3
numberSpeaker1 speaking: 4
numberSpeaker2 speaking: 4
numberSpeaker2 speaking: 5
numberSpeaker1 speaking: 5
......
这3个例子证明了synchronized 用在非静态方法上,同步锁是作用域对象级别的,并且一个对象的锁只能被一个线程所持有。
二. 静态同步方法:
public static synchronized void method(int value )
{
// doSomething;
}
synchronized 关键字用在静态方法上,表示同步锁是在静态方法所在Class对象上,Class锁说明2个线程调只要调用某个类(不管是Class.add还是new
Class().add)都会产生锁竞争的情况。
三. 非静态方法内的同步块:
有时候我们不需要同步整个方法,只需要对多线程会影响到的代码块做同步,我们应该尽量做到最细粒度的同步控制来保证程序的效率。java允许我们用synchronized 关键字对某一段需要同步的代码做同步。写法如下:
public void method(int value)
{
// doSomething;
synchronized (object)
{
// doSomething;
}
}
synchronized(实例对象),这里的实例对象可以是this,也可以是其他实例化的object,这时锁定的就是this或者object。
如果synchronized包含了方法中的所有代码,并且被锁对象是this,那么效果和synchronized标记在方法是一样的。
下面我们把非静态方法中的示例代码稍作修改,看看synchronized 锁指定对象(非this)是什么效果:
public class TestSynchronized {
public static void main(String[] args) {
String str1 = "numberSpeaker1";
String str2 = "numberSpeaker2" ;
NumberSpeaker numberSpeaker1 = new NumberSpeaker(str1);
NumberSpeaker numberSpeaker2 = new NumberSpeaker(str2);
Thread thread1 = new NumberSpeakerThread(numberSpeaker1 );
Thread thread2 = new NumberSpeakerThread(numberSpeaker2 );
thread1.start();
thread2.start();
}
}
class NumberSpeaker {
public String speakerName ;
public NumberSpeaker(String speakerName ) {
this.speakerName = speakerName ;
}
public void speak(int number) {
synchronized (speakerName ) {
for (int i = 0; i < number ; i ++) {
try {
Thread. sleep(500);
} catch (InterruptedException e ) {
e.printStackTrace();
}
System. out.println(speakerName + " speaking: " + i );
}
}
}
}
class NumberSpeakerThread extends Thread {
private NumberSpeaker numberSpeaker = null;
public NumberSpeakerThread(NumberSpeaker numberSpeaker ) {
this.numberSpeaker = numberSpeaker ;
}
@Override
public void run() {
this.numberSpeaker .speak(10);
}
}
我们可以用这段代码做细微调整来模拟2种情形:
1. 2个NumberSpeaker实例的speakerName用同一个String对象,代码修改如下:
NumberSpeaker numberSpeaker1 = new NumberSpeaker(str1 );
NumberSpeaker numberSpeaker2 = new NumberSpeaker(str1);
执行代码,结果是2个线程按顺序执行:
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 1
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 3
numberSpeaker1 speaking: 4
numberSpeaker1 speaking: 5
numberSpeaker1 speaking: 6
numberSpeaker1 speaking: 7
numberSpeaker1 speaking: 8
numberSpeaker1 speaking: 9
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 1
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 3
numberSpeaker1 speaking: 4
numberSpeaker1 speaking: 5
......
2. 2个NumberSpeaker实例的speakerName用同不同String对象,代码修改如下:
NumberSpeaker numberSpeaker1 = new NumberSpeaker(str1 );
NumberSpeaker numberSpeaker2 = new NumberSpeaker(str2);
执行代码,结果是2个线程并发执行:
numberSpeaker2 speaking: 0
numberSpeaker1 speaking: 0
numberSpeaker1 speaking: 1
numberSpeaker2 speaking: 1
numberSpeaker2 speaking: 2
numberSpeaker1 speaking: 2
numberSpeaker1 speaking: 3
numberSpeaker2 speaking: 3
numberSpeaker2 speaking: 4
numberSpeaker1 speaking: 4
numberSpeaker2 speaking: 5
numberSpeaker1 speaking: 5
......
上面的例子证明,synchronized(object)的写法中,锁定的对象是括号中的object,并且在同一时间内只有一个线程可以获取这个锁。
四. 静态方法内的同步块:
public static void method(int value)
{
// doSomething;
synchronized (Class)
{
// doSomething;
}
}
synchronized用在静态方法内部,括号内应该填写CLASS,即锁定的是CLASS级别(和用synchronized标记静态方法一样)。
我们再来看看下面的例子:
public class MyClass {
public static synchronized void log1(String msg1,
String msg2) {
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1,
String msg2) {
synchronized (MyClass.class)
{
log.writeln(msg1);
log.writeln(msg2);
}
}
}
这段代码中,log1和log2都用了synchronized ,log1是静态同步方法,所以log1的synchronized 是作用在MyClass.class上的,虽然log2的synchronized 同步的是代码块,但log2中锁的也是MyClass.class。所以在同一时间内只能有一个线程调用log1方法或者log2中的同步块。
总结:
用synchronized 时,首先要分清被锁的对象,是类的实例对象还是类本身。那么就可以很清楚的分辨什么情况会发生多线程琐竞争的情况。synchronized 可以把任何一个非null对象作为"锁",当synchronized作用在方法上时,锁住的便是对象实例(this);当作用在静态方法时锁住的便是对象对应的Class实例,因为Class数据存在于永久带,因此静态方法锁相当于该类的一个全局锁;当synchronized作用于某一个对象实例时,锁住的便是对应的代码块。
实现原理:
当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态用来区分请求的线程:
- Contention List:所有请求锁的线程将被首先放置到该竞争队列。
- Entry List:Contention List中那些有资格成为候选人的线程被移到Entry List。
- Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set。
- OnDeck:任何时刻最多只能有一个线程正在竞争锁,该线程称为OnDeck。
- Owner:获得锁的线程称为Owner。
- !Owner:释放锁的线程。
下图反映了个状态转换关系:
新请求锁的线程将首先被加入到ConetentionList中,当某个拥有锁的线程(Owner状态)调用unlock之后,如果发现EntryList为空则从ContentionList中移动线程到EntryList,并会指定EntryList中的某个线程(一般为Head)为Ready(OnDeck)线程。Owner线程并不是把锁传递给OnDeck线程,只是把竞争锁的权利交给OnDeck,OnDeck线程需要重新竞争锁。
注意事项:
synchronized的同步机制在java多线程处理上并不是最优的选择,所以在java 5开始引入并发库(java.util.concurrent)。
- [转帖]B4. Concurrent JVM 锁机制(synchronized)
B4. Concurrent JVM 锁机制(synchronized) https://www.cnblogs.com/zlxyt/p/11050346.html 挺好的 感觉这个文章写的 不过想要 ...
- B4. Concurrent JVM 锁机制(synchronized)
[概述] JVM 通过 synchronized 关键字提供锁,用于在线程同步中保证线程安全. [synchronized 实现原理] synchronized 可以用于代码块或者方法中,产生同步代码 ...
- 转:synchronized和LOCK的实现原理---深入JVM锁机制
JVM底层又是如何实现synchronized的? 目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug ...
- [转载]深入JVM锁机制-synchronized
转自:http://blog.csdn.net/chen77716/article/details/6618779,并加上少量自己的理解 目前在Java中存在两种锁机制:synchronized和Lo ...
- java多线程之:深入JVM锁机制2-Lock (转载)
前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...
- 深入JVM锁机制2-Lock
前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...
- 深入JVM锁机制1-synchronized
目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Loc ...
- 并发编程的锁机制:synchronized和lock
1. 锁的种类 锁的种类有很多,包括:自旋锁.自旋锁的其他种类.阻塞锁.可重入锁.读写锁.互斥锁.悲观锁.乐观锁.公平锁.可重入锁等等,其余就不列出了.我们重点看如下几种:可重入锁.读写锁.可中断锁. ...
- 【Java线程】锁机制:synchronized、Lock、Condition
http://www.infoq.com/cn/articles/java-memory-model-5 深入理解Java内存模型(五)——锁 http://www.ibm.com/develope ...
随机推荐
- 在AWS中部署OpenShift平台
OpenShift是RedHat出品的PAAS平台.OpenShift做为PAAS平台最大的特点是它是完全容器化的PAAS平台,底层封装了Docker和Kubernetes,上层暴露了对开发者友好的接 ...
- 转载:细说Cookie
细说Cookie 转载:http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.html 阅读目录 开始 Cookie 概述 Cookie的 ...
- Servlet3.1上传图片示例
一.前端JSP页面 <%@page pageEncoding="UTF-8"%><!DOCTYPE html><html><head> ...
- vue2项目使用axios发送请求
前言:在Vue1.0的时候有一个官方推荐的 ajax 插件 vue-resource,但是自从 Vue 更新到 2.0 之后,官方就不再更新 vue-resource. 目前主流的 Vue 项目,都选 ...
- RDMA调研报告&一点随笔
计算所科研实践随笔 被淹没在论文海里的两个星期. 早上7:10分起床,草草洗漱,7:30出发,开始漫长的1小时通勤.从地铁站的安检口起,队便排的极长,让人看得头皮发麻.下到了轨道旁稍好,但每趟呼啸而来 ...
- C4.5算法(摘抄)
1. C4.5算法简介 C4.5是一系列用在机器学习和数据挖掘的分类问题中的算法.它的目标是监督学习:给定一个数据集,其中的每一个元组都能用一组属性值来描述,每一个元组属于一个互斥的类别中的某一类.C ...
- 关于tomcat的Unsupported major.minor version 51.0问题记录
今天在构建一个应用时使用了注解的方式,可能是别的原因,正常访问一个servlet的时候报了一个从来没见过的错误. 2017-5-12 15:54:52 org.apache.catalina.core ...
- 九天学会Java,第五天,函数定义函数调用
变量和数据类型,赋值和输出 算术运算 选择结构 循环结构 函数定义,函数调用 变量作用域 栈,程序运行的基石 面向对象 异常处理 语言提供的公用包 什么是函数,为什么有函数,大家可能有这样的疑问. 举 ...
- HTML基础1
网页的基本结构(双标签):html,title 页面标题,head 网页上控制信息,body 页面内容,所有属性都写在前面. body的属性:bgcolor 背景色:text 文字颜色:margin ...
- JAVA实用案例之文件导入导出(POI方式)
1.介绍 java实现文件的导入导出数据库,目前在大部分系统中是比较常见的功能了,今天写个小demo来理解其原理,没接触过的同学也可以看看参考下. 目前我所接触过的导入导出技术主要有POI和iRepo ...