一.synchronized同步方法或者同步块

在了解synchronized关键字的使用方法之前,我们先来看一个概念:互斥锁,顾名思义:能到达到互斥访问目的的锁。

举个简单的例子:如果对临界资源加上互斥锁,当一个线程在访问该临界资源时,其他线程便只能等待。

在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。

在Java中,可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。

下面通过几个简单的例子来说明synchronized关键字的使用:

1.synchronized方法

下面这段代码中两个线程分别调用insertData对象插入数据:

public class Test {

    public static void main(String[] args)  {
final InsertData insertData = new InsertData(); new Thread() {
public void run() {
insertData.insert(Thread.currentThread());
};
}.start(); new Thread() {
public void run() {
insertData.insert(Thread.currentThread());
};
}.start();
}
} class InsertData {
private ArrayList<Integer> arrayList = new ArrayList<Integer>(); public synchronized void insert(Thread thread){
for(int i=;i<;i++){
System.out.println(thread.getName()+"在插入数据"+i);
arrayList.add(i);
}
}
}

从上输出结果说明,Thread-1插入数据是等Thread-0插入完数据之后才进行的。说明Thread-0和Thread-1是顺序执行insert方法的。

这就是synchronized方法。

不过有几点需要注意:

1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,

3)如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型,也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。

2.synchronized代码块

当在某个线程中执行这段代码块,该线程会获取对象synObject的锁,从而使得其他线程无法同时访问该代码块。

synObject可以是this,代表获取当前对象的锁,也可以是类中的一个属性,代表获取该属性的锁。

比如上面的insert方法可以改成以下两种形式:

class InsertData {
private ArrayList<Integer> arrayList = new ArrayList<Integer>(); public void insert(Thread thread){
synchronized (this) {
for(int i=;i<;i++){
System.out.println(thread.getName()+"在插入数据"+i);
arrayList.add(i);
}
}
}
} class InsertData {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Object object = new Object(); public void insert(Thread thread){
synchronized (object) {
for(int i=;i<;i++){
System.out.println(thread.getName()+"在插入数据"+i);
arrayList.add(i);
}
}
}
} 从上面可以看出,synchronized代码块使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步

另外,每个类也会有一个锁,它可以用来控制对static数据成员的并发访问。

并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。(对象级别的synchronized,还有类级别的synchronized.锁定的是整 个clz.class)

看下面这段代码就明白了:

public class Test {

    public static void main(String[] args)  {
final InsertData insertData = new InsertData();
new Thread(){
@Override
public void run() {
insertData.insert();
}
}.start();
new Thread(){
@Override
public void run() {
insertData.insert1();
}
}.start();
}
} class InsertData {
public synchronized void insert(){
System.out.println("执行insert");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行insert完毕");
} public synchronized static void insert1() {
System.out.println("执行insert1");
System.out.println("执行insert1完毕");
}
}

执行结果;

第一个线程里面执行的是insert方法,不会导致第二个线程执行insert1方法发生阻塞现象。

下面我们看一下synchronized关键字到底做了什么事情,我们来反编译它的字节码看一下,下面这段代码反编译后的字节码为:

public class InsertData {
private Object object = new Object(); public void insert(Thread thread){
synchronized (object) { }
} public synchronized void insert1(Thread thread){ } public void insert2(Thread thread){ }
}

  从反编译获得的字节码可以看出,synchronized代码块实际上多了monitorenter和monitorexit两条指令。monitorenter指令执行时会让对象的锁计数加1,而monitorexit指令执行时会让对象的锁计数减1,其实这个与操作系统里面的PV操作很像,操作系统里面的PV操作就是用来控制多个线程对临界资源的访问。对于synchronized方法,执行中的线程识别该方法的 method_info 结构是否有 ACC_SYNCHRONIZED 标记设置,然后它自动获取对象的锁,调用方法,最后释放锁。如果有异常发生,线程自动释放锁。

  

  有一点要注意:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。

二.java单例模式(双重检查加锁)的原因

双重检查加锁,只有在第一次实例化时,才启用同步机制,提高了性能。

public class Singleton{
private static Singleton instance = null;//是否是final的不重要,因为最多只可能实例化一次。
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
//双重检查加锁,只有在第一次实例化时,才启用同步机制,提高了性能。
synchronized(Singleton.Class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}

Java并发编程(四)synchronized的更多相关文章

  1. Java并发编程:Synchronized及其实现原理

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  2. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  3. Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  4. 【转】Java并发编程:synchronized

    一.什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的资源:一个变量.一个对象.一个文件.一个数据库表等,而 ...

  5. 4、Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  6. 【转】Java并发编程:Synchronized及其实现原理

    一.Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步 ...

  7. Java 并发编程(四):如何保证对象的线程安全性

    01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合 ...

  8. 【Java并发编程】synchronized相关面试题总结

    目录 说说自己对于synchronized关键字的了解 synchronized关键字的三种使用 synchronized关键字的底层原理 JDK1.6之后对synchronized关键字进行的优化 ...

  9. 【Java并发编程四】关卡

    一.什么是关卡? 关卡类似于闭锁,它们都能阻塞一组线程,直到某些事件发生. 关卡和闭锁关键的不同在于,所有线程必须同时到达关卡点,才能继续处理.闭锁等待的是事件,关卡等待的是其他线程. 二.Cycli ...

  10. Java并发编程实例(synchronized)

    此处用一个小程序来说明一下,逻辑是一个计数器(int i):主要的逻辑功能是,如果同步监视了资源i,则不输出i的值,但如果没有添加关键字synchronized,因为是两个线程并发执行,所以会输出i的 ...

随机推荐

  1. Swift5 语言指南(十五) 继承

    类可以从另一个类继承方法,属性和其他特性.当一个类继承自另一个类时,继承类称为子类,它继承的类称为其超类.继承是一种基本行为,它将类与Swift中的其他类型区分开来. Swift中的类可以调用和访问属 ...

  2. mybatis 详解(一)

    http://www.cnblogs.com/ysocean/p/7271600.html 1.什么是MyBatis? MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目 ...

  3. python修改文件中字符串并写入

    python实际工作中,做一些小工具,很方便.最近在做一个格式转换工具时候,用到了替换文件中特定字符串的 功能.当初没直接想出来,就在网上查了一下,做个记录,方便后续使用. # -*- coding: ...

  4. Testing - 软件测试杂谈

    Part-1 起步 测试是发现质量问题.分析.跟踪.推动与解决的过程. 1 熟悉业务,设计优质的测试用例,需要对所测试项目的业务需求非常熟悉 了解整个产品的研发和测试流程 全程参与,对需求.设计.开发 ...

  5. [EXP]Microsoft Windows CONTACT - Remote Code Execution

    [+] Credits: John Page (aka hyp3rlinx) [+] Website: hyp3rlinx.altervista.org [+] Source: http://hyp3 ...

  6. 纯JavaScript实现俄罗斯方块(详细注释,ES6)

    借鉴了慕课网的课程<基于websocket的火拼俄罗斯(单机版)>虽然改动比较多,但是还是核心部分没有改,加了一些不怎么好听的声音,和看起来并不好看的界面. CSS部分基本是瞎写的,因为对 ...

  7. Java中IO流中的装饰设计模式(BufferReader的原理)

    本文粗略的介绍下JavaIO的整体框架,重在解释BufferReader/BufferWriter的演变过程和原理(对应的设计模式) 一.JavaIO的简介 流按操作数据分为两种:字节流与字符流. 流 ...

  8. 【转】ASP.NET Core MVC 配置全局路由前缀

    本文地址:http://www.cnblogs.com/savorboard/p/dontnet-IApplicationModelConvention.html作者博客:Savorboard 前言 ...

  9. kubernetes构建时容器的时间与宿主机不一致的解决方法

    kubernetes默认使用docker容器部署的应用,会出现时间与主机不一致的情况 容器时间与主机差8个小时:主机的与容器的/etc/localtime不一致 解决方法:挂载主机的/etc/loca ...

  10. Spring杂记BeanFactory之getBean方法

    1.(BeanFactory) getBean(beanName) 2.(AbstractBeanFactory) doGetBean 3.(AbstractBeanFactory) transfor ...