synchronized

多线程不安全的底层原因以及两种加锁方式的区别

二次信任 提交于 2020-02-08 10:35:47
如何保证多线程的安全运行 1.线程的安全性问题体现在: 原子性:一个或者多个操作在 CPU 执行的过程中 不被中断 的特性 可见性:一个线程对共享变量的修改,另外一个线程能够 立刻看到 有序性:程序执行的顺序按照代码的 先后顺序 执行 2.导致原因: 缓存 导致的可见性问题 线程切换 带来的原子性问题 编译优化 带来的有序性问题 3.解决办法: JDK Atomic 开头的原子类、synchronized、Lock,可以解决原子性问题 synchronized、volatile、Lock,可以解决可见性问题 Happens-Before 规则可以解决有序性问题,JVM对程序运行设定的一些规则。 两种加锁方式的区别 1.这里的两种加锁方式分别指的是synchronized关键字和Lock类。 2.这两种方式的底层实现可以看: Lock底层原理 synchronized的底层实现 3.synchronized和Lock的区别: 实现层面不一样。synchronized 是 Java 关键字, JVM 层面 实现加锁和释放锁;Lock 是一个接口,在 代码层面 实现加锁和释放锁,( CAS乐观锁比synchronized更底层,是CPU原语,属于操作系统层面的 ) 是否自动释放锁。synchronized 在线程代码执行完或出现异常时 自动释放锁 ;Lock 不会自动释放锁,需要再

设计模式之单例模式

為{幸葍}努か 提交于 2020-02-08 02:00:15
一、介绍   单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 TIPS: 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。 实现的目标:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 解决得问题:一个全局使用的类频繁地创建与销毁。 核心思想:构造函数是私有的。并且对外提供一个全局的访问方法。 使用场景:  1、要求生产唯一序列号。 2、代码级别的全局的JVM缓存。(有状态的单例) 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。 二、懒汉式   所谓的懒汉式单例,是指:第一次调用才初始化,避免内存浪费。典型的时间换取空间的做法。因为第一次获取单例的时候会执行单例的实例化,浪费一些时间。但是后面再继续获取就不存时间问题了。 2.1、懒汉式的简化版 1 package com.shf.mode.singleton; 2 3 /** 4 * 描述:懒汉式 5 * 单例实例在第一次调用的时候创建 6 * 典型的以时间换取空间,第一次调用

如何避免死锁

冷暖自知 提交于 2020-02-07 04:22:46
死锁发生的条件 互斥,共享资源只能被一个线程占用 占有且等待,线程 t1 已经取得共享资源 s1,尝试获取共享资源 s2 的时候,不释放共享资源 s1 不可抢占,其他线程不能强行抢占线程 t1 占有的资源 s1 循环等待,线程 t1 等待线程 t2 占有的资源,线程 t2 等待线程 t1 占有的资源 避免死锁的方法 对于以上 4 个条件,只要破坏其中一个条件,就可以避免死锁的发生。 对于第一个条件 "互斥" 是不能破坏的,因为加锁就是为了保证互斥。 其他三个条件,我们可以尝试 一次性申请所有的资源,破坏 "占有且等待" 条件 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件 按序申请资源,破坏 "循环等待" 条件 使用管理类一次性申请所有的资源,破坏 "占有且等待" 条件示例 package constxiong.concurrency.a023; import java.util.HashSet; import java.util.Set; /** * 测试 一次性申请所有的资源,破坏 "占有且等待" 条件示例 * @author ConstXiong * @date 2019-09-24 14:04:12 */ public class TestBreakLockAndWait { //单例的资源管理类 private

java多线程

微笑、不失礼 提交于 2020-02-07 04:06:50
文章目录 多线程简介 创建多线程方法 构造方法 成员方法 多线程实现案例 1 多线程实现案例 2 线程调度 线程优先级 线程控制 休眠线程 加入线程 礼让线程 守护线程 中断线程 线程安全 判断线程安全问题存在可能性的标准 线程安全问题的解决方案:同步机制 同步方法的格式及锁对象问题 Lock 实现类 成员方法 死锁问题 部分线程安全与不安全类 线程通信 未通信导致错误案例 等待唤醒机制 等待唤醒机制实现案例 ThreadGroup 线程组 构造方法 成员方法 线程池 Executors 成员方法 ExecutorService 成员方法 线程池实现案例一:Runnable 线程池实现案例二:Callable 线程池实现案例三:Callable 带泛型 匿名内部类方式实现多线程案例 Timer 定时器 构造方法 成员方法 定时器的简单实现案例 多线程简介 进程 进程是系统进行资源分配和调用的独立单位 线程 在同一个进程中又可以同时执行多个任务,每一个任务可以看做是一个线程 线程是程序执行的单元、执行路径,也是程序使用 CPU 的基本单元 多线程及其意义 多线程不能提高程序的执行速度,但是可以使应用程序在抢占 CPU 资源时占得上风,但无法保证,线程的执行具有随机性 并行和并发 并行:逻辑上同时发生,指在某一个时间内同时运行多个程序 并发:物理上同时发生

synchronized你到底锁住的是谁?

自闭症网瘾萝莉.ら 提交于 2020-02-06 18:57:22
synchronized从语法的维度一共有3个用法: 1、静态方法加上关键字 2、实例方法(也就是普通方法)加上关键字 3、方法中使用同步代码块 前两种方式最为偷懒,第三种方式比前两种性能要好。 synchronized从锁的是谁的维度一共有两种情况: 锁住类 锁住对象实例 1)静态方法上的锁 静态方法是属于“类”,不属于某个实例,是所有对象实例所共享的方法。也就是说如果在静态方法上加入synchronized,那么它获取的就是这个类的锁,锁住的就是这个类。 2)实例方法(普通方法)上的锁 实例方法并不是类所独有的,每个对象实例独立拥有它,它并不被对象实例所共享。在实例方法上加入synchronized,那么它获取的就是这个类的对象的锁,锁住的就是这个对象实例。 synchronized(this){...} this关键字所代表的意思是该对象实例,换句话说,这种用法synchronized锁住的仍然是对象实例,他和public synchronized void demo(){}可以说仅仅是做了语法上的改变。 synchronized(Demo.class){...} 这种形式等同于抢占获取类锁,锁住的是这个类。 来源: https://www.cnblogs.com/gaopengpy/p/12269721.html

23种设计模式

女生的网名这么多〃 提交于 2020-02-06 02:57:58
23种模式java实现源码 收集五年的开发资料下载地址 : http://pan.baidu.com/share/link?shareid=3739316113&uk=4076915866#dir/path=%2Fstudy 一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下: 二、设计模式的六大原则 1、开闭原则(Open Close Principle) 开闭原则就是说 对扩展开放,对修改关闭 。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 2、里氏代换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。

设计模式(Design Patterns)

和自甴很熟 提交于 2020-02-06 01:41:11
设计模式(Design Patterns) 一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式 ,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式 ,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式 ,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类: 并发型模式和线程池模式 。用一个图片来整体描述一下: 二、设计模式的六大原则 1、开闭原则(Open Close Principle) 开闭原则就是说 对扩展开放,对修改关闭 。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 2、里氏代换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用

jvm关闭

。_饼干妹妹 提交于 2020-02-05 13:50:12
关闭方式 正常关闭 最后一个普通线程(非守护线程)结束 调用了System.exit 发送SIGINT信号(相当于不带参数的kill命令)或者键入Ctrl-C 强制关闭 调用Runtime.halt 发送SIGKILL信号(kill -9 命令) 关闭钩子(Shutdown Hook) 钩子配置方法 通过下面的设置方法可看到,关闭钩子实际为线程 Runtime . getRuntime ( ) . addShutdownHook ( new Thread ( ( ) - > { } ) ) ; 触发调用Hook线程流程 正常关闭中,JVM首先调用所有已注册的关闭钩子。JVM并不能保证关闭钩子的调用顺序。在关闭应用程序线程时,如果有线程仍然在运行,那么这些线程接下来将与关闭进程并发进行。 当所有的关闭钩子都执行结束时,如果runFinalizersOnExit为true,那么JVM将运行终结器,然后再停止。JVM并不会停止或中断任何在关闭时仍然运行的应用程序线程。当JVM最终结束时,这些线程将被强行结束。 如果关闭钩子或终结器没有执行完成,那么正常关闭进程挂起并且JVM必须被强行关闭。 –《Java并发编程实战》 上述是书里面的介绍,下面跟着源码对照整个流程: 添加钩子 Runtime: public void addShutdownHook ( Thread hook ) {

多线程安全问题

我只是一个虾纸丫 提交于 2020-02-05 05:27:08
一旦开启多线程就可能会存在线程安全问题 什么是安全问题? 这里的安全问题可以理解为代码实现在逻辑上的问题,比如:火车站卖票 100张票让4个人去卖,一定不能出现卖的票是负数问题,那么开启多线程后,如何才能保证卖的票不可能存在负数呢? 常用的解决方法有两种: 1,使用同步代码块,把需要同步的代码再放同步代码块中 2,使用同步函数 两者的区别: 同步的锁,可以理解为就是那个对象! 同步代码块的写法:synchronized (任意对象) { } 同步函数:public synchronized void show(){ },此时synchronized的锁默认的是 this,所以在同时使用同步代码块和同步函数的时候,同步代码块中放的对象必须也得是 this 才能保证线程安全 synchronized(this) 静态的同步函数:public static synchronized void show(){ },这时候的synchronized的锁是该函数所属的字节码文件对象,可以使用getClass()方法获取,也可以使用当前 类名.clas表示 来源: CSDN 作者: h1787430016 链接: https://blog.csdn.net/h1787430016/article/details/103803182

java基础面试题

一曲冷凌霜 提交于 2020-02-04 11:37:35
1.synchronized的底层原理? synchronized底层原理,是跟JVM指令和monitor有关,你如果用到了synchronized关键字,在底层的JVM指令中,会有monitorenter和monitorexit两个指令, 每个对象都有一个关联的monitor,如果要对一个对象加锁,那么必须获取这个对象关联的monitor的lock锁, monitor的锁是支持可重入的,里边会有一个一个计数器,第一次加锁时候,会把计数器加1,变成1,第二次时候,会在加一,变成2,这样就是可重入加锁,这个时候,如果其他线程来获取这个对象的锁,发现monitor的计数器不为0,意味着别人加锁了。然后这个线程就会进入block阻塞状态,什么都干不了,就是等着获取锁,如果程序执行出了synchronized的范围,会执行monitorexit指令,会对线程获取锁的那个对象的monitor计数器减1,如果有多次重入加锁就会对应多次减1,知道最后,计数器是0,然后后边阻塞的线程就可以获取锁了,同样只有一个线程可以获取到锁。 2. CAS锁的理解以及实现原理 CAS:compare and set AtomicInteger.incrementAndGet(); 线程在操作之前,会先去读取一下变量的旧值,然后把它加一,尝试赋值给变量,赋值之前会检查一下变量的值和之前获取到的值是否一致,如果一致