线程安全

彻底搞懂volatile关键字

让人想犯罪 __ 提交于 2019-11-27 15:52:08
对于volatile这个关键字,相信很多朋友都听说过,甚至使用过,这个关键字虽然字面上理解起来比较简单,但是要用好起来却不是一件容易的事。 这篇文章将从多个方面来讲解volatile,让你对它更加理解。 计算机中为什么会出现线程不安全的问题 volatile既然是与线程安全有关的问题,那我们先来了解一下计算机在处理数据的过程中为什么会出现线程不安全的问题。 大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中会涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。 为了处理这个问题,在CPU里面就有了高速缓存(Cache)的概念。当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。 我举个简单的例子,比如cpu在执行下面这段代码的时候, t = t + 1; 会先从高速缓存中查看是否有t的值,如果有,则直接拿来使用,如果没有,则会从主存中读取

多线程——彻底理解ThreadLocal

雨燕双飞 提交于 2019-11-27 13:16:48
ThreadLocal是什么   早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。   从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。   所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。 ThreadLocal的接口方法 ThreadLocal类接口很简单,只有4个方法,我们先来了解一下: void set(Object value)设置当前线程的线程局部变量的值。 public Object get()该方法返回当前线程所对应的线程局部变量。 public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 protected

java并发编程——并发容器

廉价感情. 提交于 2019-11-27 12:39:34
概述 java cocurrent包提供了很多并发容器,在提供并发控制的前提下,通过优化,提升性能。本文主要讨论常见的并发容器的实现机制和绝妙之处,但并不会对所有实现细节面面俱到。 为什么JUC需要提供并发容器? java collection framework提供了丰富的容器,有map、list、set、queue、deque。但是其存在一个不足:多数容器类都是非线程安全的,即使部分容器是线程安全的,由于使用sychronized进行锁控制,导致读/写均需进行锁操作,性能很低。 java collection framework可以通过以下两种方式实现容器对象读写的并发控制,但是都是基于sychronized锁控制机制,性能低: 1. 使用sychronized方法进行并发控制,如HashTable 和 Vector。以下代码为Vector.add(e)的java8实现代码: public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } 2.使用工具类Collections将非线程安全容器包装成线程安全容器。以下代码是Collections

单例模式

不羁岁月 提交于 2019-11-27 10:49:11
Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。   单例模式有以下特点:   1、单例类只能有一个实例。   2、单例类必须自己创建自己的唯一实例。   3、单例类必须给所有其他对象提供这一实例。 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。 在讲单例之前,要做一次基础知识的科普行动。大家都知道Java类加载器加载内容的顺序: 1、从上往下(Java的变量需要先声明才能使用) 2、静态后动态(对象实例化)(静态块和static关键字修饰在实例化以前分配内存空间) 3、先属性后方法(成员变量不能定义在方法中,只能定义在class下) 二、懒汉式单例(4种写法) 1.普通懒汉模式 package spring_singleton; /** * @author pypua * @date

全面解释java中StringBuilder、StringBuffer、String类之间的关系

偶尔善良 提交于 2019-11-27 10:22:26
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间,StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象,StringBuffer和StringBuilder类功能基本相似 1. String 类   String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。 String a = "a"; //假设a指向地址0x0001 a = "b";//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。 因此String的操作都是改变赋值地址而不是改变值操作。 2. StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。 StringBuffer buf=new StringBuffer(); //分配长16字节的字符缓冲区 StringBuffer buf=new StringBuffer(512); /

乐观锁的一种实现方式——CAS

生来就可爱ヽ(ⅴ<●) 提交于 2019-11-27 09:26:49
在 深入理解乐观锁与悲观锁 一文中我们介绍过锁。本文在这篇文章的基础上,深入分析一下乐观锁的实现机制,介绍什么是CAS、CAS的应用以及CAS存在的问题等。 线程安全 众所周知,Java是多线程的。但是,Java对多线程的支持其实是一把双刃剑。一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题。线程安全性可能是非常复杂的,在没有充足的同步的情况下,多个线程中的操作执行顺序是不可预测的。 Java里面进行多线程通信的主要方式就是共享内存的方式,共享内存主要的关注点有两个:可见性和有序性。加上复合操作的原子性,我们可以认为Java的线程安全性问题主要关注点有3个:可见性、有序性和原子性。 Java内存模型 (JMM)解决了可见性和有序性的问题,而锁解决了原子性的问题。这里不再详细介绍JMM及锁的其他相关知识。但是我们要讨论一个问题,那就是锁到底是不是有利无弊的? 锁存在的问题 Java在JDK1.5之前都是靠 synchronized 关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有共享变量的锁,都采用独占的方式来访问这些变量。独占锁其实就是一种悲观锁,所以可以说 synchronized 是悲观锁。 悲观锁机制存在以下问题: 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

Java String,StringBuffer,StringBuilder的区别

故事扮演 提交于 2019-11-27 07:38:06
主要分为两方面,一方面是运行速度,另一方面是线程安全。 运行速度: StringBuilder>StringBuffer>String String最慢是因为String是字符串常量,String的对象一旦被创建之后是不可更改的,Java实际对String的操作是一个不断创建新对象并回收旧对象的过程,所以运行速度就会慢很多,但StringBuffer,StringBuilder是字符串变量,对字符串变量进行操作,直接对该对象进行修改就可以。 线程安全: StringBuffer是线程安全的,StringBuilder是线程不安全的。 如果一个StringBuffer对象在字符串缓冲区中进行多线程操作时,StringBuffer中很多方法是可以有Synchronized关键字的,但StringBuilder的方法没有该关键字,所以多线程环境下为了保证线程安全,就要使用StringBuffer,但如果是单线程或者是不规定需要确保线程安全的情况下还是建议使用速度较快的StringBuilder。 总结: 1.String适合于少量字符串操作。 2.StringBuffer适合于多线程下在字符串缓冲区中进行大量字符串操作。 3.StringBuilder适合于单线程下在字符串缓冲区中进行大量字符串操作。 来源: https://blog.csdn.net/YangTongA/article

Java学习资料-Servlet单例多线程

与世无争的帅哥 提交于 2019-11-27 06:35:33
Servlet 单例多线程 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例); 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的<Connector>设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等。 3.当请求到达时,Servlet容器通过调度线程(Dispatchaer Thread) 调度它管理下线程池中等待执行的线程(Worker Thread)给请求者; 4.线程执行Servlet的service方法; 5.请求结束,放回线程池,等待被调用; (注意:避免使用实例变量(成员变量),因为如果存在成员变量,可能发生多线程同时访问该资源时,都来操作它,照成数据的不一致,因此产生线程安全问题) 从上面可以看出: 第一:Servlet单实例,减少了产生servlet的开销; 第二:通过线程池来响应多个请求,提高了请求的响应时间; 第三:Servlet容器并不关心到达的Servlet请求访问的是否是同一个Servlet还是另一个Servlet,直接分配给它一个新的线程;如果是同一个Servlet的多个请求

《码出高效》读书笔记

陌路散爱 提交于 2019-11-27 06:15:27
面向对象 接口 1.接口方法默认public abstract。 接口属性访问控制符默认public statistatic final。 2.接口支持多重继承,抽象类只能单继承。 3.接口可以继承接口。 方法 1.方法参数必须做校验。比如判空。 2.构造方法不能被继承,不能被重写。 3.getter和setter中不要添加业务逻辑。 4.异步用于处理耗时操作,处理完毕还可以返回处理结果。 5.在pojo中,属性变量最好设置成包装类,这样在调用时没有经过setter,直接getter会报错空指针,便于发现问题。 public class User { private Integer age; private Boolean vip; //getter、setter.. } 如果age设置成int,当调用时没有先setter,直接getter,会得到默认值0,但是不会报错。 6.构造方法不可以被继承。 7.static代码块只运行一次。 static 0.静态方法如果使用可修改的对象,那在并发时会存在线程安全的问题。 1.阿里巴巴java规范:SimpleDateFormat是线程不安全的,不要定义为static变量。如果定义为static变量,必须加锁。 声明SimpleDateFormat的时候,如果使用的是static定义的

Java中的String,StringBuilder,StringBuffer三者的区别

ぐ巨炮叔叔 提交于 2019-11-27 06:14:55
摘自:https://www.cnblogs.com/su-feng/p/6659064.html 这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。 首先说运行速度,或者说是执行速度, 在这方面运行速度快慢为:StringBuilder > StringBuffer > String   String最慢的原因:   String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。 以下面一段代码为例: 1 String str="abc"; 2 System.out.println(str); 3 str=str+"de"; 4 System.out.println(str);   如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改