Java架构学习(二)多线程线程安全synchronizedJava内存模型volatitle关键字AtomicInteger原子类

本文主要是介绍Java架构学习(二)多线程线程安全synchronizedJava内存模型volatitle关键字AtomicInteger原子类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、什么是线程安全问题?

什么是线程安全问题?
答:当多个线程共享同一个全局变量,做写的时候,可能会受到其他线程的干扰,导致
数据有问题,这种现象叫做线程安全问题。做读的时候,不会产生线程安全问题。什么时候会发生线程安全:多个线程同时共享同一个全局变量,做写的操作的时候,就会发生线程安全。多个线程共享同一个局部变量,做写的操作时候不会发生线程安全问题。

分析图:

这里写图片描述

抢票线程安全案例 下面代码会出现线程安全问题

package com.leeue;
/*** * @classDesc: 功能描述:(模仿抢票,查看线程安全问题)* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月7日 上午11:27:52**/
class CreateThread implements Runnable{int count  = 100;Object obj = new Object();public void run() {//出售火车票while(count > 0){try {Thread.sleep(200);} catch (Exception e) {// TODO: handle exception}sale();}}public void sale(){System.out.println(Thread.currentThread().getName()+"正在出售"+(100-count+1)+"张票");count--;	}}
public class ThreadeDemo {public static void main(String[] args) {CreateThread createThread = new CreateThread();Thread t1 = new Thread(createThread,"窗口001:");Thread t2 = new Thread(createThread,"窗口002:");t1.start();t2.start();}}

运行结果,出现线程安全问题如图:
这里写图片描述

2、使用同步代码块解决线程安全问题

线程是如何实现同步()? 保证数据的原子性
原子性:如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫原子性线程为什么需要实现同步?多个线程共享同一个全局变量,有数据安全性问题,保证数据的原子性。解决办法:1、使用synchroized --- 自动挡2、lock ---jdk1.5 并发包 --- 手动线程安全问题的解决思路:多个线程不要同时操作同一个局部变量做写的操作。使用同步代码快 synchronized 包裹有线程安全问题的代码

使用synchroized 解决线程安全问题 代码

package com.leeue;
/*** * @classDesc: 功能描述:(模仿抢票,查看线程安全问题)* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月7日 上午11:27:52**/
class CreateThread implements Runnable{int count  = 100;Object obj = new Object();public void run() {//出售火车票while(count > 0){try {Thread.sleep(2000);} catch (Exception e) {// TODO: handle exception}sale();}}public void sale(){synchronized (obj) {//同步代码块if(count > 0){//这个是处理最后第100张票,防止售出第101张票System.out.println(Thread.currentThread().getName()+"正在出售"+(100-count+1)+"张票");count--;}}}}
public class ThreadeDemo {public static void main(String[] args) {CreateThread createThread = new CreateThread();Thread t1 = new Thread(createThread,"窗口001:");Thread t2 = new Thread(createThread,"窗口002:");t1.start();t2.start();}}

运行结果图:

这里写图片描述

使用synchroized锁的总结

使用synchroized锁必须要有有的一些条件:1、必须要有两个线程以上,需要发生同步。2、多个线程想要同步,必须使用同一把锁,如上代码的 Object3、保证只有一个线程进行执行。同步的原理:1、首先一个线程拿到锁,其他线程已经有了cpu执行的,一直排队,等待其他线程释放锁。2、锁是什么时候释放?代码执行完毕,或者程序抛出异常的时候,锁就会被释放。3、锁已经被释放掉的话,其他线程开始抢锁,获取锁的线程进同步区。使用synchroized锁的缺点:多个线程需要判断锁,较为消耗资源,效率比较低。 、锁的资源竞争。、会产生死锁问题。

3、同步函数的使用this锁

什么是同步函数?就是在方法上加上synchroized来修饰。同步函数使用的什么锁?怎么证明?使用的this锁。  要证明:使用不同的锁就行了。

同步函数使用的是this锁代码

这里写代码片

面试题:一个线程使用同步函数,另一个线程使用的事同步代码块this能够同步吗?

可以实现同步。因为同步函数使用的锁就是this锁。

一个线程使用同步函数,另一个线程使用同步代码块(非this锁)能同步吗?

不能实现同步,因为锁不一样,同步函数使用的锁是this锁。

4、静态同步函数

在方法上面,加上synchroized 叫同步函数非静态同步函数同步函数使用的this锁。静态(static)同步函数使用的不是this锁,使用的字节码锁,.class
因为静态函数里面都不能调用this 咯当一个变量被static修饰的话,存放在用就去,当class文件被加载的时候会被初始化。记住:当一个变量被static修饰的话存放在永久区,当一个class文件被加载的你好就会被初始化。同步和加锁。加锁是为了同步。同步是为了保证数据的安全性,也就是是原子性。

注意:只有两个线程锁是一样的,才能实现同步

静态同步函数代码

package com.leeue;class CreateThread4 implements Runnable {private static int count = 100;private Object object = new Object();// 对象锁public boolean flag = true;public void run() {// 模拟抢票if (flag) {while (count > 0) {synchronized (CreateThread4.class) {// synchronized 代码块if (count > 0) {try {Thread.sleep(50);} catch (Exception e) {// TODO: handle exception}System.out.println(Thread.currentThread().getName()+ "正在出售第:" + (100 - count + 1) + "票");count--;}}}} else {while (count > 0) {sale();}}}public static  synchronized void sale() {// 窗口2  静态同步函数if (count > 0) {try {Thread.sleep(50);} catch (Exception e) {// TODO: handle exception}System.out.println(Thread.currentThread().getName() + "正在出售第:"+ (100 - count + 1) + "票");count--;}}
}public class ThreadDemo04 {public static void main(String[] args) throws InterruptedException {CreateThread4 t = new CreateThread4();Thread t1 = new Thread(t, "窗口1:");t.flag = false;Thread t2 = new Thread(t, "窗口2:");t1.start();/* Thread.sleep(50); */t2.start();}}
两个线程,一个线程使用同步函数,另一个线程使用静态同步函数能实现同步吗?
答:不能,同步函数使用的是this锁,静态同步函数使用的是当前的字节码文件。

5、多线程死锁问题

什么是死锁,多线程中死锁现象?答:同步中嵌套同步,无法释放,一致等待变为死锁。

这里写图片描述

死锁产生原因: 死锁的产生,就是同步中嵌套同步,互相不释放。线程1 先拿到同步代码块oj锁,再拿到同步函数的this锁。
线程2 先拿到同步函数的this锁,再拿到同步函数代码块的oj锁。原因:线程1需要同步函数的this锁,才能继续执行,而线程2需要线程1的oj锁才能继续执行。所以就产生了互相等待对方释放锁。产生了死锁问题。

6、多线程的三大特性

原子性:保证线程安全问题,数据一致性。
可见性:Java内存模型,线程不可见。
有序性:join wait notify  多线程之间通讯的。

7、Java内存模型

什么是Java内存模型(JMM)?属于多线程可见性 JMM,Java内存模型决定了一个线程与另一个线程是否可见。Java内存模型,分为很多区域,主内存区域:主要存放共享的全局变量私有本地内存:存放本地线程私有变量 注意:本地内存存放主内存共享数据副本就因为Java内存模型,所以就产生线程安全问题。		什么事Java内存结构?属于java内存结构jvm内存分配产生线程安全的原因:本地内存存放的是主内存共享数据的副本。在本地私有内存完成操作后,再刷新到主内存中去。

这个是就是分析

可以保证线程安全,就是要主内存 通知另一个线程

这里写图片描述

8、Volatile关键字

Volatitle:关键字作用是保证线程之间可见,但不保证原子性。

没有使用Volatitle关键字

package com.leeue;
/*** * @classDesc: 功能描述:(volatile 关键字的使用 ) 这样写,子线程可以及时的结束。* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月8日 下午2:17:23**/
class CreateThread05 implements Runnable{public boolean flag = true;public void run() {System.out.println("子线程开始执行");while(flag){}System.out.println("子线程结束");}public void setFlag(boolean flag) {this.flag = flag;}}
public class ThreadVolatile {public static void main(String[] args) {CreateThread05 t1 = new CreateThread05();Thread thread = new Thread(t1);thread.start();t1.setFlag(false);}}

运行结果

这里写图片描述

设置主线程休眠一段时间,就不会及时刷新主内存了,注意这里有两个线程,一个主线程
一是子线程。

代码

package com.leeue;
/*** * @classDesc: 功能描述:(volatile 关键字的使用)*  主线程加入了休眠,没加volatile关键字* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月8日 下午2:17:23**/
class CreateThread05 implements Runnable{public boolean flag = true;public void run() {System.out.println("子线程开始执行");while(flag){}System.out.println("子线程结束");}public void setFlag(boolean flag) {this.flag = flag;}}
public class ThreadVolatile {public static void main(String[] args) throws InterruptedException {CreateThread05 t1 = new CreateThread05();Thread thread = new Thread(t1);thread.start();Thread.sleep(1000);t1.setFlag(false);System.out.println("flag 已经设置成false");Thread.sleep(1000);System.out.println(t1.flag);}}

运行结果

这里写图片描述

加上volatile 关键字修饰变量后

package com.leeue;
/*** * @classDesc: 功能描述:(volatile 关键字的使用)* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月8日 下午2:17:23**/
class CreateThread05 implements Runnable{public volatile boolean  flag = true;public void run() {System.out.println("子线程开始执行");while(flag){}System.out.println("子线程结束");}public void setFlag(boolean flag) {this.flag = flag;}}
public class ThreadVolatile {public static void main(String[] args) throws InterruptedException {CreateThread05 t1 = new CreateThread05();Thread thread = new Thread(t1);thread.start();Thread.sleep(1000);t1.setFlag(false);System.out.println("flag 已经设置成false");Thread.sleep(1000);System.out.println(t1.flag);}}

加上volatile运行结果

这里写图片描述

注意:加上了volatile关键字后将多线程之间设为可见性,强制线程每次读取 flag的值的时候都
会区主内存中去取值。

10、volatitle 与synchronized区别?

仅靠volatitle是不能保证线程的安全性的,无法保证原子性。
1、volatitle是轻量级的,只能修饰变量,synchronized是重量级的还可以修饰方法。
2、volatitle只能保证数据的可见性,不能用来同步,因为多个线程同时访问volatitle不会发生
阻塞。
3、synchronized 不仅保证了线程的可见性,还保证了原子性。因为只有获取到了锁的线程
才能进入临界区,从而保证了所有进入临界区的语句全部执行完毕。多个线程争抢进入synchronized
会出现阻塞现象。
4、线程安全性。线程安全主要是包括了两个方面,1、可见性,2、原子性。仅仅使用volatitle是不能保证线程的安全性的,而synchronized可以实现线程的安全性。

9、AtomicInteger原子类

证明volatitle没有数据的原子性

package com.leeue;
/*** * @classDesc: 功能描述:(证明volatitle是没有原子性的程序)* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月11日 下午1:42:45**/
public class VolatitleNoAtomic extends Thread{private  static volatile int count = 0;//static 修饰的关键字,所有线程都可以共享@Overridepublic void run() {for(int i = 0; i < 1000; i++){count++;}System.out.println(getName()+":"+count);}public static void main(String[] args) {//创建10个线程VolatitleNoAtomic[] volatitleNoAtomics = new VolatitleNoAtomic[10];for(int i = 0; i < 10; i++){volatitleNoAtomics[i] = new VolatitleNoAtomic();}for(int i = 0; i <  10; i++){volatitleNoAtomics[i].start();//注意!!启动线程永远是使用start()而不是run()方法,//调用run()方法会当成一个普通的方法来调用的}}
}

运行结果

这里写图片描述

注意!!启动线程永远是使用start()而不是run()方法,调用run()方法会当成一个普通的方法来调用的注意:

使用JDK1.5并发包里面的原子类,保证数据的原子性 AtomicIntegeter()

package com.leeue;import java.util.concurrent.atomic.AtomicInteger;/*** * @classDesc: 功能描述:(证明volatitle是没有原子性的程序)* @author:李月* @Version:v1.0* @createTime:@Date 2018年6月11日 下午1:42:45**/public class VolatitleNoAtomic extends Thread{//private  static volatile int count = 0;//static 修饰的关键字,所有线程都可以共享private static AtomicInteger count = new AtomicInteger(0);//使用jdk1.5里面的并发包里面的原子类。@Overridepublic void run() {for(int i = 0; i < 1000; i++){//count++count.incrementAndGet();}//System.out.println(getName()+":"+count);System.out.println(getName()+":"+count.get());}public static void main(String[] args) {//创建10个线程VolatitleNoAtomic[] volatitleNoAtomics = new VolatitleNoAtomic[10];for(int i = 0; i < 10; i++){volatitleNoAtomics[i] = new VolatitleNoAtomic();}for(int i = 0; i <  10; i++){volatitleNoAtomics[i].start();//注意!!启动线程永远是使用start()而不是run()方法,调用run()方法会当成一个普通的方法来调用的}}
}

运行结果

这里写图片描述

AtomincInteger和synchronized都是解决线程安全性问题,保证数据的一致性也就是原子性

同步的概念:程序中的同步:是程序从上往下有顺序执行线程中的同步:是保证线程安全,保证数据的原子性。

ThreadLocal使用案例

package com.leeue.thread;/*** * @classDesc: 功能描述:(验证ThreadLocal用法)* @author:<a href="leeue@foxmail.com">李月</a>* @Version:v1.0* @createTime:2019年2月19日 下午9:56:57*/class Res {//使用ThreadLocal,给每个线程提供一个自己的局部变量ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {protected Integer initialValue() {return 0;};};public Integer getNumber() {int count = threadLocal.get() + 1;threadLocal.set(count);return count;}}public class Demo01 extends Thread {private Res res;public Demo01(Res res) {this.res = res;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {System.out.println(Thread.currentThread().getName() + "count=" + res.getNumber());}}public static void main(String[] args) {Res res = new Res();Demo01 t1 = new Demo01(res);Demo01 t2 = new Demo01(res);t1.start();t2.start();}
}

这篇关于Java架构学习(二)多线程线程安全synchronizedJava内存模型volatitle关键字AtomicInteger原子类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/380422

相关文章

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Java中的工具类命名方法

《Java中的工具类命名方法》:本文主要介绍Java中的工具类究竟如何命名,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java中的工具类究竟如何命名?先来几个例子几种命名方式的比较到底如何命名 ?总结Java中的工具类究竟如何命名?先来几个例子JD

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依