ThreadLocal
ThreadLocal简介
代码演示
package com.aiguigu.juclearn.threadpool;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import lombok.Data;
/**
* @author: Runqiang_Jiang
* @Time: 2024/3/16 15:11
*/
@Data
class House{
int saleCount=0;
public synchronized void saleHouse(){
++saleCount;
}
//
// ThreadLocal<Integer> saleValues=new ThreadLocal<Integer>(){
// @Override
// protected Integer initialValue() {
// return 0;
// }
// };
ThreadLocal<Integer> saleValues=ThreadLocal.withInitial(()->0);
public void saleVolumety(){
saleValues.set(saleValues.get()+1);
}
}
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
House house = new House();
for (int i = 0; i < 5; i++) {
new Thread(()->{
int size = new Random().nextInt(5) + 1;
// System.out.println(size);
for (int j = 0; j < size; j++) {
house.saleHouse();
house.saleVolumety();
}
System.out.println(Thread.currentThread().getName()+"\t"+"号卖出"+house.saleValues.get());
}).start();
}
TimeUnit.MILLISECONDS.sleep(300);
System.out.println(Thread.currentThread().getName()+"共计卖出:"+house.saleCount);
}
}
try{
// System.out.println(size);
for (int j = 0; j < size; j++) {
house.saleHouse();
house.saleVolumety();
}
System.out.println(Thread.currentThread().getName()+"\t"+"号卖出"+house.saleValues.get());
}finally {
house.saleValues.remove();
}
线程池的例子
package com.aiguigu.juclearn.threadlocal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author: Runqiang_Jiang
* @Time: 2024/3/16 15:32
*/
class MyData{
ThreadLocal<Integer> threadLocalValue=ThreadLocal.withInitial(()->0);
public void add(){
threadLocalValue.set(threadLocalValue.get()+1);
}
}
public class ThreadLocalDemo2 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
MyData myData = new MyData();
try{
for (int i = 0; i < 10; i++) {
threadPool.submit(()->{
Integer integer = myData.threadLocalValue.get();
myData.add();
Integer afterData = myData.threadLocalValue.get();
System.out.println(Thread.currentThread().getName()+"\t"+"beforeData:"+integer+"\t"+"afterData:"+afterData);
});
}
}catch (Exception e){
e.printStackTrace();
}
finally {
threadPool.shutdown();
}
}
}
如果不清空,值会变的越来越大
清空后
threadPool.submit(()->{
try{
Integer integer = myData.threadLocalValue.get();
myData.add();
Integer afterData = myData.threadLocalValue.get();
System.out.println(Thread.currentThread().getName()+"\t"+"beforeData:"+integer+"\t"+"afterData:"+afterData);
}finally {
myData.threadLocalValue.remove();
}
});
因为每个Thred内有自己的实例副本且该副本只由当前线程自己使用。既然其他Thred不可访问,那就不存在多线程间共享的问题。统一设置初始值,但是每个线程对这个值的修改都是各自线程互相独立的。
一句话:如何才能不争抢?
1.加入synchronized 或者Lock控制资源的顺序访问
2.人手一份,大家各自安好,没必要抢夺
ThreadLocal的源码分析
Thread、ThreadLocal以及ThreadLocalMap之间的关系
ThreadLocal的内存泄露问题
什么是内存泄漏?
不再被使用的对象或者变量占用的内存不能被回收,就是内存泄漏。
引用
强引用
package com.aiguigu.juclearn.reference;
/**
* @author: Runqiang_Jiang
* @Time: 2024/3/16 16:10
*/
class MyObject{
//这个方法一般不用复写
@Override
protected void finalize() throws Throwable {
System.out.println("----------invoke finalize method");
}
}
public class ReferenceDemo {
public static void main(String[] args) {
MyObject myObject = new MyObject();
System.out.println("gc before........"+myObject);
myObject=null;
System.gc();//人为
System.out.println("gc after........"+myObject);
}
}
软引用
public static void main(String[] args) {
SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
System.out.println("-----softRererence"+softReference.get());
System.gc();
System.out.println("----------gc after内存够用:"+softReference.get());
byte[] bytes = new byte[20 * 1024 * 1024];
System.out.println("----------gc after内存不够用:"+softReference.get());
}
虚引用
public static void main(String[] args) {
WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject());
System.out.println("-----gc before: "+weakReference.get());
System.gc();
System.out.println("-----gc after: "+weakReference.get());
}
虚引用
代码
public static void main(String[] args) {
MyObject myObject = new MyObject();
ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject,
referenceQueue);
//System.out.println(phantomReference.get());
List<byte[]> bytes=new ArrayList<>();
new Thread(()->{
while (true){
bytes.add(new byte[1*1024*1024]);
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(phantomReference.get()+"\t"+"list add success");
}
},"t1").start();
new Thread(()->{
while (true){
Reference<? extends MyObject> poll = referenceQueue.poll();
if(poll!=null){
System.out.println("有虚对象被回收,加入了队列");
break;
}
}
},"t2").start();
}
GCroots和四大引用小结
关系
为什么要用弱引用?
当前栈帧出栈,当前栈帧对应的threadlocal对象也应当被销毁,如果是强引用,该threadlocal对象就不会被回收,从而发生内存泄漏,从而key与threadlocal对象为弱引用。
总结