synchronized关键字的作用是线程同步,而线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

synchronized用法

1、 在需要同步的方法的方法签名中加入synchronized关键字

synchronized public void getValue() {
...
}

上面的代码修饰的synchronized是非静态方法,如果修饰的是静态方法(static)含义是完全不一样的。具体不一样在哪里,后面会详细说清楚。

synchronized static public void getValue() {
...
}

2、使用synchronized块对需要进行同步的代码段进行同步。

public void synchronizedMethod() {
try {
synchronized (this) {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

上面的代码块是synchronized (this)用法,还有synchronized (非this对象)以及synchronized (类.class)这两种用法,这些使用方式的含义也是有根本的区别的。我们先带着这些问题继续往下看。

对象锁与类锁

synchronized关键字的使用大致有五种情况,其中三种是对象锁,两种是类锁:

  • synchronized修饰非静态方法、同步代码块的synchronized (this)用法和synchronized (非this对象)的用法锁的是对象锁。
  • synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类锁。

下面看一些例子,首先看一下线程不同步的情况:

public class SynchronizedTest {
public void synchronizedMethod() {
try {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public class MyThread extends Thread {
private SynchronizedTest synchronizedTest; public MyThread(SynchronizedTest synchronizedTest) {
super();
this.synchronizedTest = synchronizedTest;
} @Override
public void run() {
super.run();
synchronizedTest.synchronizedMethod();
}
} public class Main {
public static void main(String[] args) throws InterruptedException {
SynchronizedTest synchronizedTest1 = new SynchronizedTest(); Thread a = new MyThread(synchronizedTest1);
a.setName("a");
a.start(); Thread b = new MyThread(synchronizedTest1);
b.setName("b");
b.start();
}
}

运行结果:

Thread[a,5,main]begin at:2017-09-13 16:52:54
Thread[b,5,main]begin at:2017-09-13 16:52:54
Thread[a,5,main]end at:2017-09-13 16:52:56
Thread[b,5,main]end at:2017-09-13 16:52:56

可以看到两个线程交叉执行,要让这两个线程依次执行,则需要使用对象锁同步,可以将SynchronizedTest类修改成下面的三种方式来添加对象锁:

public class SynchronizedTest {
synchronized public void synchronizedMethod() {
try {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
或者
public class SynchronizedTest {
public void synchronizedMethod() {
try {
synchronized (this) {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
或者
public class SynchronizedTest { Object object = new Object(); public void synchronizedMethod() {
try {
synchronized (object) {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果:

Thread[a,5,main]begin at:2017-09-13 16:59:12
Thread[a,5,main]end at:2017-09-13 16:59:14
Thread[b,5,main]begin at:2017-09-13 16:59:14
Thread[b,5,main]end at:2017-09-13 16:59:16

从上面可以看出,synchronized代码块(后两种方式)使用起来比synchronized方法(第一种方式)要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步。

如果将Main类修改成下面这样,则对象锁失效:

public class Main {
public static void main(String[] args) throws InterruptedException {
SynchronizedTest synchronizedTest1 = new SynchronizedTest();
SynchronizedTest synchronizedTest2 = new SynchronizedTest(); Thread a = new MyThread(synchronizedTest1);
a.setName("a");
a.start(); Thread b = new MyThread(synchronizedTest2);
b.setName("b");
b.start();
}
}

运行结果:

Thread[b,5,main]begin at:2017-09-13 17:03:26
Thread[a,5,main]begin at:2017-09-13 17:03:26
Thread[b,5,main]end at:2017-09-13 17:03:28
Thread[a,5,main]end at:2017-09-13 17:03:28

因为上面两个线程调用的是两个对象中的方法,对象锁是不起作用的,这种情况下应该使用类锁,可以将SynchronizedTest类修改成下面的两种方式来添加类锁:

public class SynchronizedTest {
public void synchronizedMethod() {
try {
synchronized (SynchronizedTest.class) {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
或者
public class SynchronizedTest {
synchronized public static void synchronizedMethod() {
try {
System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
Thread.sleep(2000);
System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果:

Thread[a,5,main]begin at:2017-09-13 17:07:02
Thread[a,5,main]end at:2017-09-13 17:07:04
Thread[b,5,main]begin at:2017-09-13 17:07:04
Thread[b,5,main]end at:2017-09-13 17:07:06

需要特别说明:

对于同一个类A,线程1争夺A对象实例的对象锁,线程2争夺类A的类锁,这两者不存在竞争关系。也就说对象锁和类锁互不干预。

静态方法则一定会同步,非静态方法需在单例模式才生效,但是也不能都用静态同步方法,总之用得不好可能会给性能带来极大的影响。另外,有必要说一下的是Spring的bean默认是单例的。

Java并发—synchronized关键字的更多相关文章

  1. Java并发——synchronized关键字

    前言: 只要涉及到Java并发那么我们就会考虑线程安全,实际上能够实现线程安全的方法很多,今天先介绍一下synchronized关键字,主要从使用,原理介绍 一.synchronized的使用方法 1 ...

  2. 精通java并发-synchronized关键字和锁

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages synchronized关键字和锁 示例代码 public class MyThreadTest2 { public ...

  3. Java并发-Synchronized关键字

    一.多线程下的i++操作的并发问题 package passtra; public class SynchronizedDemo implements Runnable{ private static ...

  4. Java 多线程 —— synchronized关键字

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  5. 从分布式锁角度理解Java的synchronized关键字

    分布式锁 分布式锁就以zookeeper为例,zookeeper是一个分布式系统的协调器,我们将其理解为一个文件系统,可以在zookeeper服务器中创建或删除文件夹或文件.设D为一个数据系统,不具备 ...

  6. java基础Synchronized关键字之对象锁

    java中Synchronized关键字之对象锁    当有多个线程对一个共享数据进行操作时,需要注意多线程的安全问题. 多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同 ...

  7. Java的synchronized关键字:同步机制总结

    JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程 ...

  8. java中synchronized关键字分析

    今天我们来分析一下java中synchronized关键字.首先来看一段java代码:(本地编译环境为mac,jdk1.8的环境) Demo.java package com.example.spri ...

  9. Java基础-synchronized关键字的用法(转载)

    synchronized--同步 顾名思义是用于同步互斥的作用的. 这里精简的记一下它的使用方法以及意义: 当synchronized修饰 this或者非静态方法或者是一个实例的时候,所同步的锁是加在 ...

随机推荐

  1. Arduino IDE for ESP8266 (4)局域网 网页图形化控制灯 路由系统

    用到的路由系统文件(备用) 链接:https://pan.baidu.com/s/1bqR7Lc7 密码:7w2z 教程http://www.windworkshop.cn/?p=1274

  2. (转)解决k8s集群提示docker login问题(同样适用于Rancher)

    文章转自 https://blog.liv1020.com/ 参考文档:https://kubernetes.io/docs/concepts/containers/images/#configuri ...

  3. 学习Karma+Jasmine+istanbul+webpack自动化单元测试

    学习Karma+Jasmine+istanbul+webpack自动化单元测试 1-1. 什么是karma?  Karma 是一个基于Node.js的Javascript测试执行过程管理工具.该工具可 ...

  4. Vue2 第二天学习

    个人小总结:1年多没有写博客,感觉很多知识点生疏了,虽然工作上能解决问题,但是当别人问到某个知识点的时候,还是迷迷糊糊的,所以坚持写博客是硬道理的,因为大脑不可能把所有的知识点记住,有可能某一天忘了, ...

  5. 栈(stack)信息

    栈在JVM虚拟机中是线程的一块私有空间,比如存储函数的调用信息.局部变量等 特性 先进后出和后进先出即FIFO 借用网络的一个图,感觉看完就可以了解了 最先调用的函数压入栈低,最后压入得函数在栈顶,函 ...

  6. Mybatis学习总结(二)——Mapper代理开发

    一.概要 1.原始DAO开发中存在的问题:(1)DAO实现方法体中存在很多过程性代码.(2)调用SqlSession的方法(select/insert/update)需要指定Statement的id, ...

  7. Android 让图片等比例缩放的三种方法

    方法一:客户端等比例 前提条件:服务器端需要返回原始图片的“宽和高”或者“宽高缩放比例”,客户端要显示的图片的宽或者高只要其一是固定的(例如:高度为200,宽度未知,或者高度为400宽度未知) 在这种 ...

  8. Hive 数据的导入导出

    数据的导入: 通过文件导入,使用load命令 一.导入本地文件: load data local inpath '/home/hadoop/files/emp.txt' overwrite into ...

  9. Ionic App之国际化(2) json数组的处理

    在Ionic App值国际化(1)中我们实现了对单个参数的多语言处理,下面开始如何进行数组的处理. 1.在我们的多语言文件中设置要访问的json数组,en.json和zh.json,此处就以en.js ...

  10. Luogu P1129 [ZJOI2007]矩阵游戏

    题目意思还是比较直观的,而且这个建模的套路也很明显. 我们首先考虑从主对角线可以转移到哪些状态. 由于每一次操作都不会把同一行(列)的黑色方块分开.因此我们发现: 只要找出\(n\)个黑色棋子,让它们 ...