IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 如何确保线程安全 -> 正文阅读

[Java知识库]如何确保线程安全

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAeG1oLXN4aC0xMzE0,size_10,color_FFFFFF,t_70,g_se,x_16充分利用CPU资源,为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰。要了解多线程,首先要了解串行和并行的概念,这样才能更好地理解多线程。

?

串行

?

串行其实是相对于单条线程来执行多个任务来说的,就拿下载文件来举个例子:当下载多个文件时,在串行中它是按照一定的顺序去进行下载的,也就是说,必须等下载完A文件之后才能开始下载B文件,它们在时间上是不可能发生重叠的。

?

并行

?

下载多个文件,开启多条线程,多个文件同时进行下载,严格意义上的,在同一时刻发生的,并行在时间上是重叠的。(即同一时刻下载A、B文件。)

?

简单了解了多线程,接 下来说说确保线程安全。

?

什么是线程安全?

既然是线程安全问题,那么肯定是在多个线程访问的情况下产生的,没有按照我们预期的行为执行,那么线程就不安全了。也就是说我们想要确保在多线程访问的时候,我们的程序还能按照我们预期的行为去执行,那么就是线程安全。

?

首先看一段线程代码,看看是不是线程安全的,代码如下:

?

/**

?* @Author 安仔夏天勤奋

?* Create Date is 2019/3/28

?* Des

?*/

public class TestThread {

? ? private static class XRunnable implements Runnable{

? ? ? ? private int count;

? ? ? ? @Override

? ? ? ? public void run() {

? ? ? ? ? ? for(int i=0;i<5;i++){

? ? ? ? ? ? ? ? getCount();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? private void getCount(){

? ? ? ? ? ? count++;

? ? ? ? ? ? //打印 计数值

? ? ? ? ? ? System.out.println(""+count);

? ? ? ? }

? ? }

? ? public static void main(String []arg){

? ? ? ? XRunnable runnable = new XRunnable();

? ? ? ? Thread a_thread = new Thread(runnable);

? ? ? ? Thread b_thread = new Thread(runnable);

? ? ? ? Thread c_thread = new Thread(runnable);

? ? ? ? a_thread.start();

? ? ? ? b_thread.start();

? ? ? ? c_thread.start();

? ? }

}

打印出的结果是

?

1

2

2

4

5

6

7

8

9

10

11

12

13

14

15

Process finished with exit code 0

从代码上看出,启动三个线程,每个线程都是循环5次得出顺序是1到15的结果。从结果可以看到,出现了两个2,出现这种情况显然表明这个方法根本就不是线程安全的,出现这种问题的原因有很多。

?

如何确保线程安全?

既然存在线程安全的问题,那么肯定得想办法解决这个问题,怎么解决?说说常见的几种方式。先上图,后分析。

?

?

确保线程安全.png

不可变(final)

在java语言中,不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施。final关键字修饰的类或数据不可修改,可靠性最高。如 String类,Integer类。

?

线程封闭

把对象封装到一个线程里,只有一个线程能看到这个对象,那么这个对象就算不是线程安全的,也不会出现任何线程安全方面的问题。线程封闭有三种:

?

Ad-hoc 线程封闭

Ad-hoc线程封闭是指维护线程封闭性的职责完全由程序实现来承担。Ad-hoc线程封闭是非常脆弱的,因为没有任何一种语言特性,例如可见性修饰符或局部变量,能将对象封闭到目标线程上。事实上,对线程封闭对象(例如,GUI应用程序中的可视化组件或数据模型等)的引用通常保存在公有变量中。

?

ThreadLocal线程封闭

它是一个特别好的封闭方法,其实ThreadLocal内部维护了一个map,map的key是每个线程的名称,而map的value就是我们要封闭的对象。ThreadLocal提供了get、set、remove方法,每个操作都是基于当前线程的,所以它是线程安全的。

?

堆栈封闭

堆栈封闭其实就是方法中定义局部变量。不存在并发问题。多个线程访问一个方法的时候,方法中的局部变量都会被拷贝一份到线程的栈中(Java内存模型),所以局部变量是不会被多个线程所共享的。

?

同步

悲观锁

非阻塞同步(乐观锁)

锁优化(过度优化)

悲观锁

同步的最常用的方法是使用锁(Lock),它是一种非强制机制,每个线程在访问数据或资源之前首先试图获取锁,并在访问结束之后释放锁;在锁已经被占用的时候试图获取锁时,线程会等待,直到锁重新可用。

?

synchronized

synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性,。可重入,修饰(class、obj、代码块)。

可重入:一个函数被重入,表示这个函数没有执行完成,但由于外部因素或内部因素,又一次进入该函数执行。一个函数称为可重入的,表明该函数被重入之后不会产生任何不良后果。可重入是并发安全的强力保障,一个可重入的函数可以在多线程环境下放心使用。

注意:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。

Lock.lock()/Lock.unLock()

ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。

ReentrantLock也是通过互斥来实现同步。在基本用法上,ReentrantLock与synchronized很相似,他们都具备一样的线程重入特性。

class RRWTextLock<Data> {

? ? ? private final Map<String, Data> map = new TreeMap<>();

? ? ? private final ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();

? ? ? private final Lock r = rrwl.readLock(); //读锁

? ? ? private final Lock w = rrwl.writeLock(); //写锁

?

? ? ? public Data get(String key) {

? ? ? ? ? r.lock();

? ? ? ? ? try { return map.get(key); }

? ? ? ? ? finally { r.unlock(); }

? ? ? }

? ? ? public String[] getAllKeys() {

? ? ? ? ? r.lock();

? ? ? ? ? try { return (String[]) map.keySet().toArray(); }

? ? ? ? ? finally { r.unlock(); }

? ? ? }

? ? ? public Data put(String key, Data value) {

? ? ? ? ? w.lock();

? ? ? ? ? try { return map.put(key, value); }

? ? ? ? ? finally { w.unlock(); }

? ? ? }

? ? ? public void clear() {

? ? ? ? ? w.lock();

? ? ? ? ? try { map.clear(); }

? ? ? ? ? finally { w.unlock(); }

? ? ? }

? }

synchronize和锁都可以保证可见性。

?

可见性

?

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 11:43:27  更:2022-05-10 11:43:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:02:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码