目录

线程安全【一】

1.线程不安全是什么?

2.线程不安全的成因

3.解决线程不安全之一Synchronized关键字(监视器锁)

1.Synchronized使用方法

2.锁对象是什么?

3.锁对象的练习

1.定义两个线程分别自增五万次

4.Synchronized的特性

1.互斥性

2.刷新内存

3.可重入

5.总结

4.解决线程不安全之一Volatile关键字

1.首先我们来分析一下CPU对代码的执行

2.将变量用volatile修饰

3.缓存一致性协议

4.内存屏障

5.内存有序性,原子性

5.synchronized和volatile关键字的区别

6.wait和notify关键字

7.wait()方法和sleep()方法的区别

8.Java中线程安全的类


线程安全一的博客在这里喔~

线程安全【一】

1.线程不安全是什么?

如果多线程环境下代码运行的结果与单线程环境下不一致时就会出现线程不安全的问题

2.线程不安全的成因

3.解决线程不安全之一Synchronized关键字(监视器锁)

加锁之后把并行变为了串行,所以实现了内存可见,因为串行一定是内存可见的

1.Synchronized使用方法

2.锁对象是什么?

3.锁对象的练习

1.定义两个线程分别自增五万次

public class Demo18 {public static void main(String[] args) throws InterruptedException {Count01 count = new Count01();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count.increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = "+count.count);}
}class Count01{public int count = 0;public void increment() {synchronized (this) {count++;}}
}

是同一个Count对象,所以线程安全,结果正确

4.Synchronized的特性

1.互斥性

同一时间只有一个线程可以获取到锁,其他的线程都要阻塞等待

2.刷新内存

线程修改完变量的值后,会把新值写回主内存,但这是用并行转为串行的方式做到的

3.可重入

Synchronized是可以嵌套使用的,当前线程每重新获取一次锁,计数器就+1,每释放一次锁,计数器就-1,直至计数器为0,当前线程才会真正的释放锁

5.总结

synchronized可以解决原子性,内存可见性,但是不能解决有序性的问题

并且synchronized并没有真正的通过线程之间的通信解决内存可见性,也没有解决有序性的问题

4.解决线程不安全之一Volatile关键字

1.首先我们来分析一下CPU对代码的执行

我们创建两个线程,一个线程不停的循环判断标识位,用第二个线程来修改标识位

代码展示:

public static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {System.out.println("t1开始============");while (count == 0) {//to do}System.out.println("t1结束============");});t1.start();Thread t2 = new Thread(() -> {System.out.println("t2开始============");System.out.println("请输入一个数字:");Scanner sc = new Scanner(System.in);count = sc.nextInt();System.out.println("t2结束============");});TimeUnit.SECONDS.sleep(1);t2.start();}

结果我们发现,在第二个线程修改了标识位之后,第一个线程并没有停止运行

2.将变量用volatile修饰

public static volatile int count = 0;

此时我们发现程序可以正常退出了,证明t1及时接收到了其他线程对变量修改后的值,解决了内存可见性的问题

不建议使用:

3.缓存一致性协议

可以理解为一种通知机制,正是这个协议的存在解决了内存不可见的问题

4.内存屏障

对加了volatile的变量,加了以下内存屏障

当某个线程对变量发生写操作之后,就会通过缓存一致性协议来通知其他的线程缓存值失效

 所以volatile可以解决内存可见性

5.内存有序性,原子性

 内存有序性是指在保证程序正确的前提下,编译器,CPU对指令的优化过程

volatile可以解决有序性问题

用volatile修饰的变量,就是要告诉编译器,不需要对这个变量所涉及到的操作做任何优化,从而实现有序性

volatile只用来修饰变量

volatile不具备原子性

通过synchronized和volatile关键字的搭配使用解决了原子性,内存可见性,有序性的问题

5.synchronized和volatile关键字的区别

6.wait和notify关键字

wait()方法和nitify()方法都是Object类中的方法

wait 做的事情:

  • 使当前执行代码的线程进行等待. (把线程放到等待队列中)
  • 释放当前的锁
  • 满足一定条件时被唤醒, 重新尝试获取这个锁. 

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常. 

wait 结束等待的条件:

  • 其他线程调用该对象的 notify 方法.
  • wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间). 
  • 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.

notify 方法是唤醒等待的线程. 

  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")
  • 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

notify方法只是唤醒(CPU随即调度,并无先来后到)某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程(所有线程共同参与锁的竞争).

在使用过程中,wait和notify必须是同一个锁对象,且wait和notify被调用后,当前线程都会释放锁资源

join()是Thread类中定义的方法

7.wait()方法和sleep()方法的区别

 线程1调用sleep()方法后,进入休眠,线程进入阻塞等待的状态,此时并不会释放锁,也不会去执行其他线程,等到等待时间结束后,线程1会继续执行后面的代码块

线程一在调用wait(10000)方法之后,线程一会进入等待状态,在等待的这十秒钟时间内,CPU可以去调用别的线程,等到等待时间结束后且CPU正在调度的线程结束释放锁之后,CPU才会回来继续执行线程一的代码块

8.Java中线程安全的类

Java 标准库中很多都是线程不安全的. 这些类可能会涉及到多线程修改共享数据, 又没有任何加锁措施.  
  • ArrayList
  • LinkedList
  • HashMap
  • TreeMap
  • HashSet
  • TreeSet
  • StringBuilder
但是还有一些是线程安全的. 使用了一些锁机制来控制.
  • Vector (不推荐使用)
  • HashTable (不推荐使用)
  • ConcurrentHashMap
  • StringBuffer

【Java EE 初阶】如何保证线程安全二相关推荐

  1. java EE初阶 —进程与线程的区别

    文章目录 1.为什么会引入进程和线程的概念 2.进程和线程的关系 2.1 坤坤吃鸡演示进程和线程区别 2.1.1 线程的安全问题 2.1.2 线程的异常问题 1.为什么会引入进程和线程的概念 引入进程 ...

  2. 详细讲解 —— 多线程的四个案例、单例模式、阻塞队列、定时器、线程池(Java EE初阶)(万字长文)

    多线程 1 wait和notify 1.1 wait()方法 1.2 notify()方法 \ notifyAll()方法 1.3 wait 和 sleep 的对比(面试题) 2. 多线程案例 2.1 ...

  3. 详细讲解 —— 网络编程套接字(Java EE初阶)

    网络编程套接字 1 认识网络编程套接字 2 UDP 数据报套接字编程 2.1 UPD服务端 2.1 UDP客户端 2.3 结果测试 3 TCP流套接字编程 3.1 TCP服务端 3.2 TCP客户端 ...

  4. Java EE初阶---网络原理之TCP_IP

    1.网络基础 1.1 认识IP地址 概念         IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址. 作用         IP地址是IP协 ...

  5. Java EE初阶---HTML

    1.HTML 结构 1.1 认识 HTML 标签 HTML 代码是由 "标签" 构成的. 形如: <body>hello</body> 标签名 (body) ...

  6. Java之List系列--ArrayList保证线程安全的方法

    原文网址:Java之List系列--ArrayList保证线程安全的方法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java中的ArrayList.LinkedList如何进行线程安全的操作.为 ...

  7. 【Java成王之路】EE初阶第二十三篇: HTTP协议和Tomcat

    目录 前言 HTTP 协议 什么是HTTP协议 回顾一下应用层协议 理解 HTTP 协议的工作过程 抓包工具的使用 HTTP协议的详细格式 + 信息 HTTP请求的格式 HTTP响应的格式 HTTP协 ...

  8. 【Java成王之路】EE初阶第十篇:(网络编程) 4

    上节回顾 套接字,TCP版本的套接字 API ServerSocket 服务器这边使用的 Socket: 服务器和客户端都需要使用 对于服务器来说: 1.创建ServerSocket 关联上一个端口号 ...

  9. 【Java成王之路】EE初阶第二十四篇: Servlet

    Sevlet Servlet 是什么 Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一 个 web app. Tomcat 是一个 ...

最新文章

  1. 16 条 yyds 的代码规范
  2. opengl 粒子按轨迹运动_袁讲经典4:一个粒子在电场中的运动轨迹相关问题
  3. react 的props和state
  4. ap忘记管理ip地址怎么办_什么是无线AP?胖瘦AP如何区分?
  5. tensorflow-tf.train.shuffle_batch
  6. ITK:计算代码点之间的时间
  7. Robert C. Martin关于UML、CASE的观点
  8. Elasticsearch Scale Out
  9. 人脸识别智能会议签到系统
  10. Windows10安装Jmeter(图文教程)
  11. inter Fortran安装匹配VS2012
  12. 大数据平台层级架构图
  13. phpstudy mysql局域网访问_phpstudy 局域网访问
  14. html网页设计的难点,在做设计与制作网页中主要难点是什么?
  15. USB-SC-09(假冒PL2303HXA芯片)WIN7-64位驱动之终极大法
  16. 金额数字转换(小写转大写)
  17. nrf52+RFX2401的PA+LNA方案,基于softdevice驱动
  18. 【科普】如何评价供应商的MES系统
  19. 那个能打的百度,回来了
  20. 【NLP】Python NLTK获取文本语料和词汇资源

热门文章

  1. nil在linux中的含义,使用NIL表示空节点的目的是什么?
  2. 金属氧化物半导体场效应晶体管的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  3. 100个问题搞定Java虚拟机
  4. c++ MP4文件解析
  5. 子网掩码换算成IP地址清单
  6. Typecho博客 通过修改后台管理员昵称 来减少安全隐患
  7. 稼说送张琥 ---博观而约取
  8. osgearth开发三维地球
  9. 罪犯用 Skype 躲避警察
  10. android四大组件基础介绍及打电话,发短信简单应用 单元测试