前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java内存模型,多线程三大特性,volatile,Threalocal,线程池

java内存模型,多线程三大特性,volatile,Threalocal,线程池

作者头像
斯文的程序
发布2019-11-07 17:20:02
9500
发布2019-11-07 17:20:02
举报
文章被收录于专栏:带你回家带你回家

目标:

1.多线程三大特性

2.java内存模型

3.volatile 作用

4.volatile 与synchronized 的区别

5.ThreadLocal 底层

6.线程池的介绍,作用以及创建方式

一、多线程三大特性

1.原子性

事物有原子性,这个概念大概都清楚,即一个操作或多个操作要么执行的过程中不被任何因素打断,要么不执行。

如何实现原子性?

通过同步代码块synchronized 或者local 锁来确保原子性

2.可见性

当多个线程共享同一个变量时,其中一个线程修改了这个变量,其他线程能够立即看到修改后的值。

如何实现可见性?(这里如果不理解java内存模型就可能不会明白后面会讲到)

使用volatile 关键字来实现线程之间的可见性。

为什么要实现可见性?

当线程拿到全局变量时,会临时存在当前线程的内存当中进行操作。相当于这个线程的局部变量,这个对其他线程是不可见的。因为cpu 只会执行一个。所以当一个线程的数据修改后,还没有及时更新到全局变量中去,而另外一个线程就执行,所以造成数据冲突。也就是线程安全问题。所以要实现这个线程之间的可见性。

3.有序性

线程按顺序执行,线程中有代码,即让代码按顺序执行。优化代码。

如何实现有序性?

通过join 方法,让其他线程等待。

二、java 内存模型

共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

java 内存模型 即 通过JMM 来控制 本地内存跟主内存之间的交互。也就是提供可见性。

例子:

这里可以看到,全局变量 flag 已经变为 false,为什么那个线程还没结束?主线程也没有结束。

这就是线程之间不可见造成的。这也是Java内存模型。

主线程,跟子线程 ,两个都有自己的本地内存。没有及时跟全局变量刷新主内存。而线程之间又不可见造成了线程不安全问题。那么如何去控制这个本地内存跟主内存的刷新呢?这就是java 内存模型,JMM

如何解决这种问题呢?

三、volatile 关键字

作用:使变量在线程之间可见,也就是能及时刷新到主内存中去

volatile 不具备原子性,虽然他解决了线程之间的可见问题,但是不能保证原子性。

保证原子性就是 synchronized 或者锁来保证的。

三、volatile 与synchronized 的区别

仅靠volatile不能保证线程的安全性。(原子性)

①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法

②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。

synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

线程安全性

线程安全性包括两个方面,①可见性。②原子性。

从上面自增的例子中可以看出:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。

四、AtomicInteger 原子类

java 并发包里面的类

例子:

五、ThreadLocal

什么是ThreadLocal?

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal的实现原理

ThreadLocal 通过map 集合

Map.put(“当前线程”,值);

六、线程池

什么是线程池?

顾名思义,线程的集合,

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

作用:

基于以下几个原因在多线程应用程序中使用线程是必须的:

1. 线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。

2. 线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。

3. 线程池根据当前在系统中运行的进程来优化线程时间片。

4. 线程池允许我们开启多个任务而不用为每个线程设置属性。

5. 线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。

6. 线程池可以用来解决处理一个特定请求最大线程数量限制问题。

线程池四种创建方式

Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

企业中一般用第二种。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档