java多线程浅析

注:日常学习记录贴,下面描述的有误解的话请指出,大家一同学习。

线程安全

一、 什么是线程安全

就是我们所说的线程同步的意思:
  多线程的存在就是压榨cpu,提高程序性能,还能减少一定的设计复杂度。

  当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问。
也就是:
  如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

        在这里我们就需要简单解读下java的内存模型

  1.Java内存模型规定了所有的变量都存储在主内存中,此处的主内存仅仅是虚拟机内存的一部分,而虚拟机内存也仅仅是计算机物理内存的一部分(为虚拟机进程分配的那一部分)。

  2.Java内存模型分为主内存,和工作内存。主内存是所有的线程所共享的,工作内存是每个线程自己有一个,不是共享的,所以只有存在共享数据时才需要考虑线程安全问题。

  3.每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作(读取、赋值),都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者之间的交互关系如下图:

二、线程3个核心概念

1.原子性

  这一点,跟数据库事务的原子性概念差不多,即一个操作(有可能包含有多个子操作)要么全部执行(生效),要么全部都不执行(都不生效)。

2.可见性

  可见性是指,当多个线程并发访问共享变量时,一个线程对共享变量的修改,其它线程能够立即看到。可见性问题是好多人忽略或者理解错误的一点。

  CPU从主内存中读数据的效率相对来说不高,现在主流的计算机中,都有几级缓存。每个线程读取共享变量时,都会将该变量加载进其对应CPU的高速缓存里,修改该变量后,CPU会立即更新该缓存,但并不一定会立即将其写回主内存(实际上写回主内存的时间不可预期)。此时其它线程(尤其是不在同一个CPU上执行的线程)访问该变量时,从主内存中读到的就是旧的数据,而非第一个线程更新后的数据。

这一点是操作系统或者说是硬件层面的机制,所以很多应用开发人员经常会忽略。

3.顺序性

  顺序性指的是,程序执行的顺序按照代码的先后顺序执行。

三、解决方法

1.加锁

(1) 锁能使其保护的代码以串行的形式来访问,当给一个复合操作加锁后,能使其成为原子操作。一种错误的思想是只要对写数据的方法加锁,其实这是错的,对数据进行操作的所有方法都需加锁,不管是读还是写。

(2) 加锁时需要考虑性能问题,不能总是一味地给整个方法加锁synchronized就了事了,应该将方法中不影响共享状态且执行时间比较长的代码分离出去。

(3) 加锁的含义不仅仅局限于互斥,还包括可见性。为了确保所有线程都能看见最新值,读操作和写操作必须使用同样的锁对象。

2.不共享状态

(1) 无状态对象: 无状态对象因为不包含任何域,也不包含任何对其他类中域的引用,计算过程中的临时状态仅存在于线程栈的局部变量中,并且只能由正在执行的线程访问,当前线程不会影响到其他正在运行的线程,所以无状态对象一定是线程安全的。

(2) 无状态的成员变量:在有状态的对象中存在无状态的成员变量,也就是该变量没有被操作。

3.不可变对象

可以使用final修饰的对象保证线程安全,由于final修饰的引用型变量(除String外)不可变是指引用不可变,但其指向的对象是可变的,所以此类必须安全发布,也即不能对外提供可以修改final对象的接口。

4.使用线程安全对象存储数据

比如

ConcurrentHashMap:

引进了segment(桶)概念,其实一个segment就是一个hashmap,每个segment的读写都是高度自治的,segment之间不相互影响,称之为"锁分段技术"。

ThreadLocal:

ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量

5.多线程无锁队列

一个负责添加数据,一个负责处理数据。 多线程无锁队列还是有锁的,只不过是用了cpu层面的CAS(Compare & Set,或是 Compare & Swap)原子操作,用到这个操作,只需要在取队列元素和添加队列元素的时候利用CAS原子操作,就可以保证多个线程对队列元素的有序存取;
小结:
1.无锁队列只适合两个线程并行使用,一个压入数据,一个弹出数据 2.无锁队列是没有锁的并行,没有死锁的危险 3.无锁队列中head和tail只有在计算结束之前的时候才能进行自增运算

转载于:https://my.oschina.net/yangok/blog/1919757

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值