发布于2021-05-29 20:52 阅读(1618) 评论(0) 点赞(25) 收藏(4)
ThreadLocal使用方法
1.set(T) : 将变量存放到线程中
2.get() : 从线程中取得私有变量
3.remove() : 从线程中移除私有变量(脏读,内存溢出)在任何场景下都有意义
4.initialValue:初始化
5.withinitialValue:初始化
ThreadLocal使用场景
1,线程安全问题
2,线程级别的数据传递
ThreadLocal缺点:
1.不能实现父子线程间的数据传递
2.脏数据---->ThreadLocal +线程池(复用)
3.内存溢出
分析内存溢出:
线程池是长生命周期的,而线程是执行完任务就结束了(线程相关的资源都会释放掉)
hashmap使用的是链表法,ThreadLocalMap使用的是开放寻址法。
开放寻址法:hash之后得到位置i,然后判断i是否为空,如果为空直接插入,不为空一直向后查找,直到找到为空的位置插入
为什么这样实现?
开放寻址法它的特点和使用场景是数据量比较少的情况下性能更好,而HashMap里面存储的数据通常情况下是比较多,这个时候使用开放寻址法效率就比较低了。
链表---->红黑树
升级:
1.链表长度大于8;
2.数组长度大于64;
降级:链表长度小于6的时候
Thread----->ThreadLocalMap------>Entry[ ]------->key , value(value是强引用的)
ThreadLocal(横店本地人) ThreadLocal(剧本扮演者)
Thread(数组)---->ThreadLocalMap (本地人的房子) ---->Entry(剧本)---->key , value(剧本的实际角色)
1.强引用Object Obj = new Object();即使发生00M也不会进行垃圾回收。
2.软引用,它的引用关系仅此于强引用。如果内存够用,那么垃圾回收不会考虑回收次引
用,将要发生00M的时候才会回收此引用。
3.弱引用:不管内存够不够用,下一次垃圾回收都会将此引用的对象回收掉。
4.虚引用:创建既回收,它可以触发一个垃圾回收的回调。
答:为了最大程度的避免OOM
为什么会发生OOM?
ThreadPool长生命周期的,----->thread不释放,----->thread拥有ThreadLocalMap不释放------>Entry[ ]不释放----->Entry里面有------->key , value(value强引用)即使发生00M也不会进行垃圾回收。
使用remove()。
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadLocalMain83 {
//1m大小的对象
static class OOMObject{
private byte[] bytes=new byte[1*1024*1024];
}
static ThreadLocal<OOMObject> threadLocal=new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor=new ThreadPoolExecutor(10,
10,0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
for (int i = 0; i <5 ; i++) {
int finalI=i;
executor.execute(new Runnable() {
@Override
public void run() {
try {
OOMObject oomObject = new OOMObject();
//set threadLocal
System.out.println("任务:" + finalI + "执行了");
threadLocal.set(oomObject);
//不用对象了
oomObject = null;
}finally {
//解决内存溢出问题
threadLocal.remove();
}
}
});
Thread.sleep(200);
}
}
}
提升程序的性能:
1.多线程
2.单例模式-------------->设计模式:
单例模式:饿汉方式(上来直接创建对象), 线程安全的
懒汉方式
线程安全的,不用加锁也是安全的
public class ThreadMain84 {
static class Singleton {
// 1.创建私有的构造函数(防止其他类直接创建)
private Singleton() {
}
// 2.定义私有变量(线程安全)
private static Singleton singleton = new Singleton();
// 3.提供公共的获取实例的方法
public static Singleton getInstance() {
return singleton;
}
}
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton);
}
}
饿汉缺点分析:程序启动之后就会创建,但是创建完了之后又可能不会使用,从而浪费了系统资源。
懒汉方式:当程序启动之后,并不会进行初始化,而是在什么时候调用什么时候初始化。
非安全的单例模式-------懒汉方式
/**
* 懒汉方式 v1 不安全
*/
public class ThreadMain85 {
static class Singleton{
//1,创建一个私有的构造方法(方法其他地方直接初始化)
private Singleton(){
}
//2.创建一个私有类对象
private static Singleton singleton=null;
//3.提供统一访问入口(方法)
public static Singleton getInstance(){
if(singleton==null){
//第一次访问
singleton=new Singleton();
}
return singleton;
}
public static void main(String[] args) {
//创建第一个对象
Singleton s1=Singleton.getInstance();
//创建第二个对象
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2);
}
}
}
线程不安全的解决方案:
方案一:性能不佳
方案二: 双重效验锁
/**
* 懒汉方式 v3
*/
public class ThreadMain88 {
static class Singleton{
//1,创建一个私有的构造方法(方法其他地方直接初始化
private Singleton(){
}
//2.创建一个私有类对象(volatile)
private static volatile Singleton singleton=null;
//3.提供统一访问入口(方法)
public static Singleton getInstance() {
if(singleton==null) {
synchronized (Singleton.class) {
if (singleton == null) {
//第一次访问
singleton = new Singleton();
}
}
}
return singleton;
}
private static Singleton s1=null;
private static Singleton s2=null;
public static void main(String[] args) throws InterruptedException {
//创建新线程执行任务
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
s1= Singleton.getInstance();
}
});
t1.start();
//使用主线程执行任务
s2= Singleton.getInstance();
//等待t1执行完
t1.join();
System.out.println(s1==s2);
}
}
}
有问题:
1.先在内存中开辟空间(买房)
2.初始化(装修)
3.将变量singleton指向内存区域(入住)
CPU指令优化(指令重排序):所以一定要加 volatile
指令重排序(前)1------->2-------->3
指令重排序(后)1------->3-------->2
生产者消费者模型——————生产者生产数据,消费者消费生产者生产的数据。
生产数据------添加数据------当数据量满了之后,不要尝试给队列添加数据了,而是阻塞等待------sleep(time)不适合wait()wait() / notify /notifyAll
消费者消费数据------取出数据-------当队列为空的时候,就阻塞等待
自定义阻塞队列------->链表 数组
/**
* 自定义阻塞队列(使用数组)
*/
import java.util.Random;
public class ThreadMain89 {
static class MyBlockingQueue {
private int[] values;//实际存储数据的数组
private int first;//队首
private int last;//队尾
private int size;//实际队列元素的大小
public MyBlockingQueue(int initial) {
//初始化变量
values = new int[initial];
first = 0;
last = 0;
size = 0;
}
//添加元素(队尾)
public void offer(int val) {
synchronized (this) {
//判断边界值
if (size == values.length) {
//队列已满
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//添加元素到队尾
values[last++]=val;
size++;
// 判断是否为最后一个元素
if(last==values.length){
last=0;
}
// 尝试唤醒消费者
this.notify();
}
}
//查询方法
public int poll() {
int result = -1;
synchronized (this) {
//判断边界值
if (size == 0) {
//队列为空,阻塞等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//取元素
result=values[first++];
size--;
//判断是否为最后一个元素
if(first==values.length){
first=0;
}
//尝试唤醒生产者
this.notify();
}
return result;
}
}
public static void main(String[] args) {
MyBlockingQueue myBlockingQueue=new MyBlockingQueue(100);
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
//每隔500毫秒生产一条数据
while (true){
int num=new Random().nextInt(10);
System.out.println("生产了随机数:"+num);
myBlockingQueue.offer(num);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
//创建消费者
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
while (true){
int result=myBlockingQueue.poll();
System.out.println("消费量数据:"+result);
}
}
});
t2.start();
}
}
原文链接:https://blog.csdn.net/qq_54850622/article/details/117262232
作者:niceboty
链接:http://www.javaheidong.com/blog/article/207452/43e937db0e8cd324283e/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!