再说synchronized关键字之前,我们首先先小小的了解一个概念-内置锁。

什么是内置锁?

在java中,每个java对象都可以用作synchronized关键字的锁,这些锁就被称为内置锁,每个对象的锁的信息都存在对象头中

所以synchronized关键字在使用过程中之所以能够保证线程的安全,也是因为使用了锁。下面就说说synchronized具体的几种用法,及使用何种类型的内置锁。

(一)用synchronized关键字修饰实例方法,示例代码:

/**
* synchronized 作用在实力方法上时,此时的内置锁为当前对象,即this
*/
public synchronized void fun1 () { }

当synchronized关键字作用在实例方法上时,此时的内置锁为当前的对象-thsi。

(二)用synchronized关键字修饰静态方法,示例代码:

 /**
* synchronized 作用在静态方法上时,此时的内置锁为当前类的字节码(Xxx.class)所对应Class对象
*/
public static synchronized void fun2 () { }

当synchronized关键字作用在静态方法(类方法)上时,此时的内置锁为当前类的字节码对应的Class对象,及Xxx.class所对应的对象。

(三)用synchronized关键字修饰代码块,示例代码:

 /**
* synchronized 作用在代码块时,此时的内置锁为括号中的对象
*/
public static synchronized void fun3 () { synchronized (Main.class){
}
}

当作用在代码块上时,内置对象为括号中的对象,此时的括号是必须要有的,因为在代码块中,synchronized必须要接收一把锁来保证线程的安全,这里对象可以是任意的java对象,如你可以自己创建一个obj传入也是没有问题的。

由于每次的锁的获取和释放总是会耗费时间,所以在synchronized中优化中也存在了偏向锁,轻量级锁等。

偏向锁:很多情况下,竞争锁时不是多个线程,而是单个线程在访问,如果此时依然按照多个线程来获取和释放锁,就会在消耗资源,所以在java的对象头中的Mark Word中就会存储偏向锁的信息,偏向锁会偏向于第一次进来的线程,如果在运行过程中不存在其他线程使用同步锁的情况下,那么就会给当前线程添加偏向锁标记。它不会每次执行都获取释放锁,当遇到其他线程竞争该锁时,该偏向锁会被挂起,jvm将会消除该锁的偏向锁标记,使其变成标准的轻量级锁。偏向锁适用于始终只有一个线程执行任务的情况。可以通过命令 开启偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

关闭偏向锁:-XX:-UseBiasedLocking

轻量级锁:轻量级锁是偏向锁失败升级得来的,它会简单的将对象头部作为指针,自旋的指向持有锁的线程堆栈内部,以此来判断一个线程是否持有对象锁,如果线程获取到轻量级锁成功,该线程可以顺利的进入到临界区执行,如果失败,则表示其他线程已经抢夺到了所,那么当前的轻量级锁就会膨胀成为重量级锁。

单例模式与线程安全问题。

在单利模式的懒汉式下,由于所执行的都是原子性操作,所以不会产生线程安全问题,但是当在饿汉式的模式下,由于非原子性操作,所以在多线程的环境下就会产生安全问题,产生非单例的对象。

所以一般在写多线程下的单例模式时,都采用的是双重判断加锁的机制方式来实现单例模式。实例代码:

package com.wangx.thread.t3;

/**
* 单利模式与线程安全
*/
public class Singleton { /**
* 使用volatile可以防止指令重排序的情况下产生的线程安全问题
*/
private volatile static Singleton singleton;
private Singleton() { } /**
* 使用双重判断加锁的可以避免线程安全问题
* @return
*/
public static Singleton getInstance() {
if (singleton == null ) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

使用双重判定是因为在第一个if为空的情况下,可能两个线程同时进入了if代码块,但是当前一个线程获取锁并创建了对象之后,后一个语句块由于是在第一个if语句块中,所以会继续往下执行,创建新的对象,产生了安全问题,所以在synchronized代码块中还需要再进行一次判断。使用volatile关键字修饰属性是因为在虚拟机的执行过程中由于性能优化,会进行指令重排序,如第一步申请了内存空间,第二部需要在空间上创建实例,第三步将对象引用指向实例空间,但是由于指令重排序可能会在申请空间之后就将对象引用指向空间,此时的对象还会创建,但是singleton 已经指向了空间,此时如果有其他线程进来刚好执行到第一个if语句时,singleton已经不为空了,就会产出线程安全问题,所以使用volatile可以禁止指令重排序,会对该变量的写操作设置一道屏障,在该变量没有赋值完成之前,不允许其他线程对该变量进行读操作。

volatile的使用及原理:

volatile在多线程的环境下保证了变量的修改对每个线程可见,可以轻量级的保证线程安全问题,比如在A线程中修改了a变量的值,那么该修改在线程b中是可见的,a变量一旦发生改变完成,那么b线程就能立刻读取到a改变后的值,这样就实现了线程之间通信的一个概念,保证线程的安全,相比较synchronized而已,volatile只能够保证一些原子性操作的变量的值一致性。在运用可以使用volatile来告诉另一个线程是否可以执行等操作。

volatile实现的原理是,在多个处理器处理任务时,cpu会将任务缓存到处理器中,这样可以加快任务的执行效率,但是到资源被写回到内存中时,所有处理器上缓存的该资源都将会失效,需要重新从内存中读取资源,为了保证数据一致性。而volatile关键字的作用就是将每个变量经处理器执行之后立刻写回到内存中,此时其他处理器失效,重新重内存中读取该资源,实现了变量在线程中的可见性,但是根据原理也可以看出,使用volatile也会降低处理器的执行效率,进而影响到程序的执行速度。所以在开发中volatile的使用也是需要注意和思考取舍的。

原文 并发编程学习笔记(3)----synchronized关键字以及单例模式与线程安全问题

并发编程学习笔记(3)----synchronized关键字以及单例模式与线程安全问题的更多相关文章

  1. Java并发编程学习笔记

    Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...

  2. 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理

    (1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...

  3. JUC并发编程学习笔记

    JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...

  4. 并发编程学习笔记(15)----Executor框架的使用

    Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...

  5. 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理

    1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...

  6. 并发编程学习笔记(13)----ConcurrentLinkedQueue(非阻塞队列)和BlockingQueue(阻塞队列)原理

    · 在并发编程中,我们有时候会需要使用到线程安全的队列,而在Java中如果我们需要实现队列可以有两种方式,一种是阻塞式队列.另一种是非阻塞式的队列,阻塞式队列采用锁来实现,而非阻塞式队列则是采用cas ...

  7. 并发编程学习笔记(11)----FutureTask的使用及实现

    1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...

  8. 并发编程学习笔记(12)----Fork/Join框架

    1. Fork/Join 的概念 Fork指的是将系统进程分成多个执行分支(线程),Join即是等待,当fork()方法创建了多个线程之后,需要等待这些分支执行完毕之后,才能得到最终的结果,因此joi ...

  9. 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理

    在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...

随机推荐

  1. MFC自己主动获取网络地址函数实现----广播地址,网关,子网掩码

    void CSetSignalBoxDlg::OnBnClickedButtonGetbroadcastaddr() {       //凝视部分为还有一种获取IP方式,可略过 //char Name ...

  2. luajit利用ffi结合C语言实现面向对象的封装库

    luajit中.利用ffi能够嵌入C.眼下luajit的最新版是2.0.4,在这之前的版本号我还不清楚这个扩展库详细怎么样,只是在2.04中,真的非常爽.  既然是嵌入C代码.那么要说让lua支持 ...

  3. oracle 11g GRID 中 关于 OLR 须要知道的一些内容

     oracle 11g GRID 中 关于 OLR 须要知道的一些内容 1.检查olr 的状态: [root@vmrac1 ~]# ocrcheck -local Status of Oracle ...

  4. js的调用函数前先执行某语句问题

    js的调用函数前先执行某语句问题 标签: web前端面试 2015-09-29 17:48 1455人阅读 评论(0) 收藏 举报  分类: js(5)  版权声明:本文为博主原创文章,未经博主允许不 ...

  5. S5P4418裸机开发系列教程--源代码下载

    S5P4418裸机系列教程之stdio S5P4418裸机系列教程之shell命令行 S5P4418裸机系列教程之串口回显 S5P4418裸机系列教程之复位測试 S5P4418裸机系列教程之led跑马 ...

  6. java 学习第一步---安装JDK以及配置环境变量

    1.下载jdk 链接:https://pan.baidu.com/s/1FiTGhxdHK0KTFawdkLT26g    提取码:zcy0    我已经在官网上面下载了1.8的jdk,通过百度云盘分 ...

  7. Python爬虫开发【第1篇】【beautifulSoup4解析器】

    CSS 选择器:BeautifulSoup4 Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据. pip 安装:pip instal ...

  8. MVC 用户权限HttpContext.User.IsInRole()

    这几天在用MVC做一个项目,用到了HttpContext.User.IsInRole() 这个方法,但是每次当我用的时候,HttpContext.User.IsInRole(“Admin”) 返回的永 ...

  9. 解决ES集群状态异常教程(存在UNASSIGNED)

    解决ES集群状态异常教程(存在UNASSIGNED)_百度经验 https://jingyan.baidu.com/article/9158e00013f787a255122843.html

  10. linux 和win7 双系统模式下 忘记win7 密码的修改方法

    首先登陆linux系统,在linux 系统下找到win7系统的安装盘(比如为C盘), (1)进入到C://windows/system32下 找到osk.exe 文件,并将其剪切到其他的地方(记住这个 ...