synchronized,是Java语言的关键字,读['siŋkrənaizd],当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

一、Java为何要使用synchronized?

线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用synchronized加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题。所以需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么就没有同步的必要。

二、synchronized修饰范围

  1. 修饰实例方法 > 多个线程访问同一个实例的加锁方法时,会出现锁的竞争

  2. 修饰静态方法 > 多个线程访问类的加锁方法时,会出现锁的竞争

  3. 修饰代码块 > 多线程访问到同一个代码块时,会出现竞争的问题

三、synchronized同步锁对象

synchronized可以用来修饰方法或代码块,我们可以把获取的同步锁归为以下3种:

  1. 实例对象锁:修饰在普通方法上(非静态方法);在代码块中修饰this即synchronized(this)代码块
  2. 类对象锁:修饰在静态方法上;在代码块中修饰class即synchronized(X.class)代码块
  3. 同步块非当前实例对象锁:在代码块中修饰非当前实例对象,比如在X类中synchronized(对象a)的代码

对这3种不同的锁,使用相互之间不受影响,对于同一种锁,会出现锁的竞态条件。

四、synchronized同步锁使用

对于1.实例对象锁的使用:

  • 所有的非静态同步方法用的都是同一把锁—实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁。
  • 实例对象锁存在于当前实例,不同的实例对象的锁相互之间不受影响。
  • synchronized(this)代码块获取的是该实例对应的锁,与非静态的synchronized同步方法使用的是同一把锁,会出现锁的竞争。
  • 实例锁和类对象锁没有影响,不会造成彼此阻塞。

对于2.类对象锁的使用:

  • 对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。
  • 而所有的静态同步方法用的也是同一把锁——类对象本身,如果一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
  • 不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,他们都使用的是同一个类的对象锁,会出现锁的竞争。
  • synchronized(X.class)代码块使用的是类Class的对象锁,与类中静态同步方法会出现锁的竞争。多个类中的synchronized(X.class)代码块也会出现锁的竞态条件。

对于3.同步块非当前实例对象锁的使用:

  • 对于同步块,由于其对象锁是可以选择的,只有使用同一把锁的同步块之间才有着竞态条件。
  • 同步锁的对象是基于实际对象而不是对象引用的,所以使用时特别注意,在锁的作用域中因改变实际对象引用从而引起锁的对象改变导致同步锁失去竞太条件。

五、synchronized使用小结

关于锁和同步的使用,汇总以下几个要点:
1、同步锁只能通过同步方法去保证共享变量安全,而不是同步变量和类。
2、当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4、对于出现竞态条件锁的线程,一个线程获取了锁,其他线程会阻塞等待该锁的释放去获取该锁。
5、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6、线程睡眠时,它所持的任何锁都不会释放。
7、线程可以获得多个重进入(synchronized )锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁
10、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问。
 

3.测试case代码

package com.test.synchroniz;

public class Service {

    public Service(){
System.out.println("当前线程:" + Thread.currentThread().getName() + " 构造方法");
} static{
System.out.println("当前线程:" + Thread.currentThread().getName() + " 静态代码块");
} private Object object1 = new Object();
private Object object2 = new Object(); synchronized public static void printA(){
try{
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法A");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法A");
}catch(Exception e){
System.out.println(e);
}
} synchronized public void printB(){
try{
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法B");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法B");
}catch(Exception e){
System.out.println(e);
}
} public void printC(){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法C");
try{
synchronized(object1){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + "进入方法C--synchronized{X}");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法C-synchronized{X}");
}
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法C");
}catch(Exception e){
System.out.println(e);
}
} public void printD(){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法D");
try{
synchronized(object2){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + "进入方法D--synchronized{X}");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法D-synchronized{X}");
}
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法D");
}catch(Exception e){
System.out.println(e);
}
} public void printE(){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法E");
try{
synchronized(this){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法E--synchronized{this}");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法E-synchronized{this}");
}
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法E");
}catch(Exception e){
System.out.println(e);
}
} public static void printF(){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法E");
try{
synchronized(Service.class){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法F--synchronized{class}");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法F-synchronized{class}");
}
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法F");
}catch(Exception e){
System.out.println(e);
}
} public void printG(){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法G");
try{
synchronized(Service.class){
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 进入方法G--synchronized{class}");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法G-synchronized{class}");
}
System.out.println("当前线程:" + Thread.currentThread().getName() + Thread.currentThread().getId() +"在 " + System.currentTimeMillis() + " 退出方法G");
}catch(Exception e){
System.out.println(e);
}
} }
package com.test.synchroniz;

/**
* 0.synchronized同步方法、synchronized静态同步方法分别是用到的是实例锁,类锁,一个线程获取到synchronized同步方法的锁时,
* 另一线程依然可以进入synchronized静态同步方法(实例锁,类锁两者不同,相互不影响
* )
* 1.synchronized同步方法,synchronized(this)都是对象锁,对于其他线程调用synchronized同步方法,synchronized(this)呈阻塞状态 </br>
* 2.同一时间同一线程只有一个线程获取对象锁执行 </br>
*
* 1.synchronized(非this)对象锁,对于非this如果是同一对象,两个线程同时只有一个可以获取该锁 </br>
* 2.对象锁(synchronized同步方法 或 synchronized(this))、synchronized(非this)对象锁 两个线程同时执行,都可获得各自的锁 </br>
*
* 1.synchronized修饰static方法与synchronized(X.class)作用一样
*
* @author fugaoyang
*
*/
public class TestRun { public static void main(String[] args) throws Exception {
Service service = new Service();
Thread threadA = new Thread("A"){
@Override
public void run(){
service.printA();
}
}; Thread threadB = new Thread("B"){
@Override
public void run(){
service.printB();
}
}; Thread threadC = new Thread("C"){
@Override
public void run(){
service.printC();
}
}; Thread threadD = new Thread("D"){
@Override
public void run(){
service.printD();
}
}; Thread threadE = new Thread("E"){
@Override
public void run(){
service.printE();
}
}; Thread threadF = new Thread("F"){
@Override
public void run(){
service.printF();
}
}; Thread threadG = new Thread("G"){
@Override
public void run(){
service.printG();
}
}; threadA.start();
//threadB.start();
//threadC.start();
//threadD.start();
//threadE.start();
threadF.start();
threadG.start(); threadA.join();
threadF.join();
threadG.join();
}
}
 
本篇文章主要总结了synchronized同步锁的使用,同步锁的原理和机制后续会做深入了解和学习。
 
本文参考部分出处:
3.《Java多线程编程核心技术-高洪严》
 

Java并发之synchronized使用的更多相关文章

  1. 深入理解Java并发之synchronized实现原理

    深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...

  2. Java并发之synchronized

    Java多线程同步关键词是常用的多线程同步手段.它可以修饰静态类方法,实例方法,或代码块.修饰static静态方法时是对整个类加锁. 一.实现原理 在JVM中对象内存分三块区域,对象头.实例数据.对齐 ...

  3. Java并发之synchronized关键字深度解析(二)

    前言 本文继续[Java并发之synchronized关键字深度解析(一)]一文而来,着重介绍synchronized几种锁的特性. 一.对象头结构及锁状态标识 synchronized关键字是如何实 ...

  4. 《提升能力,涨薪可待》—Java并发之Synchronized

    Synchronized简介 线程安全是并发编程中的至关重要的,造成线程安全问题的主要原因: 临界资源, 存在共享数据 多线程共同操作共享数据 而Java关键字synchronized,为多线程场景下 ...

  5. Java并发之Synchronized机制详解

    带着问题阅读 1.Synchronized如何使用,加锁的粒度分别是什么 2.Synchronized的实现机制是什么 3.Synchronized是公平锁吗 4.Java对Synchronized做 ...

  6. Java并发之synchronized关键字深度解析(一)

    前言 近期研读路神之绝世武学,徜徉于浩瀚无垠知识之海洋,偶有攫取吉光片羽,惶恐未领略其精髓即隐入岁月深处,遂急忙记录一二,顺备来日吹cow之谈资.本小系列为并发之亲儿子-独臂狂侠synchronize ...

  7. Java并发之synchronized关键字

         上篇文章我们主要介绍了并发的基本思想以及线程的基本知识,通过多线程我们可以实现对计算机资源的充分利用,但是在最后我们也说明了多线程给程序带来的两种典型的问题,针对它们,synchronize ...

  8. Java并发之synchronized深入

    一句话总结synchronized: JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质. 一.synchron ...

  9. java并发之synchronized详解

    前言 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同 ...

随机推荐

  1. 【转】ArcGIS Server10.1安装常见问题及解决方案

    转载自:http://www.higis.cn/Tech/tech/tId/85/ 最近因为更换系统的原因,重新安装了ArcGISServer 10.1.过程中遇到了几个小问题,虽然都一一解决了,但也 ...

  2. BottomBar之Android底部菜单

    BottomBar之Android底部菜单 前言:开源项目BottomBar,实现Android底部菜单(常用菜单,BottomBar实现动画(上下式)+消息菜单,BottomBar+ViewPage ...

  3. react 反模式——不使用jsx动态显示异步组件

    前言: react反模式 (anti-patterns)指的是违背react思想(flux)的coding方式. 本文在 App 组件中,通过 Model.show 动态显示 Model 组件,通过 ...

  4. CentOS 附加软件包

    本人初学 CentOS,安装软件与 windows 下区别很大,大部分得通过 yum install xxx .这有个问题,一方面 yum 资源有限,另一方面 yum 默认装的版本较低.比如 Cent ...

  5. 使用cocostudio 需要在Android.mk文件的配置

    直接贴上Android.mk文件吧. 对了,是cocos2d3.0的,不知道2.x是否一样. LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LO ...

  6. 如何在 Linux 虚拟机上扩展根文件系统

    问题描述 通过 Azure 平台部署的 Linux 虚拟机默认的根文件系统容量有限,需要进行扩展. 问题分析 由于 Azure 平台部署的 Linux 虚拟机默认根文件系统容量比较小,客户在使用过程中 ...

  7. cookie的初识和运用(js和jq)

    cookie是什么 cookie是浏览器提供的一种机制,它将document 对象的cookie属性提供给JavaScript.可以由JavaScript对其进行控制,而并不是JavaScript本身 ...

  8. Suse LAMP setup

    This page will describe the steps you have to take to install LAMP, which stands for Linux Apache Ma ...

  9. Oracle案例02——ORA-12034: "SCOTT"."USER_TABLE" 上的实体化视图日志比上次刷新后的内容新

    最近同事在交接工作时,发现有几个schedule job没有执行成功,我这边给看了下,其中一个是由于数据库迁移,调用dblink的host主机IP在tnsnames中没有变更导致,还有一个是无法视图的 ...

  10. MUI实现上拉加载和下拉刷新

    编写存储过程分页(此处使用T-SQL) CREATE PROC [dbo].[Common_PageList] ( @tab nvarchar(max),---表名 @strFld nvarchar( ...