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. idea 多项目部署碰到的问题

    在使用idea部署多个maven项目的时候,出现了各种坑.一天的时间有一半的时间花在了部署环境.运行环境上.把遇到的坑记录下 1.引入maven的依赖包 当项目多的时候,为了加速项目的开发,习惯性的把 ...

  2. androidwebview网页显示事件

    @Overridepublic void onPageCommitVisible(WebView view, String url) { super.onPageCommitVisible(view, ...

  3. Charles下载和使用

    下文作为备份.来源:https://www.cnblogs.com/rrl92/p/7928770.html 1. Charles下载地址 地址:https://www.charlesproxy.co ...

  4. Asp ose.Tota l for .NET 2015

    How to license Aspose.Total for .NET products Add "License.cs" [C#] OR "License.vb&qu ...

  5. vue + element-ui 制作下拉菜单(可配置路由、可根据路由高亮list、可刷新自动展开定位路由)

    本篇文章分享一篇关于 vue制作可路由切换组件.可刷新根据路由定位导航(自动展开).可根据路由高亮对应导航选项 一.实现的功能如下: 1.可折叠导航面板 2.点击导航路由不同组件           ...

  6. Struts2学习-Ioc学习-spring

    1.面向对象写法(带着面向过程的思维)电脑 computer = new 电脑(); [电脑代码中 new 打印机()]computer.打印文本("hello 140"); 电脑 ...

  7. nfs 服务器

    1.创建共享目录 #mkdir /home/hellolinux/nfs 2.创建或修改/etc/exports文件 #vi /etc/exports home/hellolinux/nfs 192. ...

  8. codeforces 814D An overnight dance in discotheque

    题目链接 正解:贪心. 首先我们可以计算出每个圆被多少个圆覆盖. 很显然,最外面的圆是肯定要加上的. 然后第二层的圆也是要加上的.那么第三层就不可能被加上了.同理,第四层的圆又一定会被加上. 然后我们 ...

  9. shell脚本中针对sudo等密码输入的问题解决方案

    一.不希望手动输入的办法 1.安装expect工具:sudo apt-get install tcl tk expect 2.脚本文件内容如下: #! /usr/bin/expectspawn sud ...

  10. [objc explain]: Non-fragile ivars

    [objc explain]: Non-fragile ivars   (2009-01-27 09:30 PM)   Non-fragile instance variables are a hea ...