多线程的使用场景

多线程的使用

使用线程池ExecutorService exe.execute来开始线程,thread,runabble,callabble都可以,isTerminated来判断线程池的线程是否都执行完毕

    @Test
    public void testThread() throws InterruptedException {
    	//创建线程池
        ExecutorService exe = Executors.newFixedThreadPool(50);
        for (int i = 1; i <= 1000; i++) {
        	//执行线程
            exe.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("1-->"+System.currentTimeMillis());
                }
            });
            //执行线程
            exe.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("2-->"+System.currentTimeMillis());
                }
            });
            //执行线程
            exe.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("3-->"+System.currentTimeMillis());
                }
            });
            //执行线程
            exe.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("4-->"+System.currentTimeMillis());
                }
            });
        } 

		//关闭线程池
        exe.shutdown();

        while (true) {
        	//判断线程是否都执行完毕
            if (exe.isTerminated()) {
                System.out.println("结束了!");
                break;
            }
            //睡眠一会再检查是否执行完毕
            Thread.sleep(200);
        }
       
    }

使用场景:生成报告,要填充数据、表格、图表,但是大数据量生成报告时间太久。
优化方案:使用多线程同时进行执行获取数据的sql和使用代码生成表格、生成图表、插入数据,然后判断线程都执行完毕后,再执行整合插入的方法,来缩短报告生成时间

ThreadLocal和InheritableThreadLocal

ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本,但是存在一个问题,就是子线程获取该值的时候为Null,可以用InheritableThreadLocal代替ThreadLocal,依旧会为每个线程提供一个独立的副本,而不会共享

  1. ThreadLocal
    @Test
    public void test2() throws InterruptedException {

        ThreadLocal num=new ThreadLocal();
        num.set(100);

        new Thread(()->{
            System.out.println(num.get());
        }).start();

        Thread.sleep(1000);
        System.out.println("完成");
    }

可以看到子线程获取的值为空
在这里插入图片描述
2. InheritableThreadLocal

    @Test
    public void test2() throws InterruptedException {
        //ThreadLocal变量无法传到子线程中使用,InheritableThreadLocal就是为了解决这个问题
        InheritableThreadLocal num=new InheritableThreadLocal();
        num.set(100);

        new Thread(()->{
            System.out.println("thread-before:"+num.get());
            num.set(200);
            System.out.println("thread-after:"+num.get());

        }).start();

        Thread.sleep(1000);

        System.out.println("main:"+num.get());

        System.out.println("完成");
    }

正常取到值
在这里插入图片描述

ThreadLocal

ThreadLocal引起的内存泄漏

ThreadLocal涉及到静态内部类ThreadLocalMap,ThreadLocalMap的键是弱引用,而值是强引用,垃圾回收的时候,会将键回收,导致值没法被回收,所以一直存在,如果要解决ThreadLocal引起的内存泄漏,就要使用完以后,手动进行remove()。

ThreadLocalMap为什么键是弱引用,而值是强引用

通过 ThreadLocal 实例自身作为键,结合每个线程内部的 ThreadLocalMap 来实现线程局部变量的存储和访问。
线程局部变量的值通常是需要被长时间使用的对象,如用户数据、数据库连接等。如果这些值是弱引用,它们可能会在GC时被回收,从而影响程序的正常运作。

多个线程都使用ThreadLocal,岂不是每个线程都用的是同一个ThreadLocal对象,那怎么实现变量副本

ThreadLocal中通过Thread.currentThread()获取当前正在执行的线程的引用,当多个线程访问同一个ThreadLocal对象时,它们实际上是在访问各自线程不同的内存空间
在这里插入图片描述

如果手动实现一个类似ThreadLocal,怎么实现

要自己实现类似ThreadLocal的功能,可以通过以下步骤来实现:

  1. 创建线程局部变量:首先,需要创建一个类来表示线程局部变量。这个类可以包含一个私有的Map对象,用于存储每个线程对应的值。可以使用ThreadLocal作为键,将线程ID与线程局部变量的值关联起来。

  2. 初始化线程局部变量:在类的构造函数中,可以初始化线程局部变量。可以将当前线程的ID作为键,将初始值作为值存储到Map中。

  3. 获取和设置线程局部变量:为了方便地获取和设置线程局部变量的值,可以在类中提供相应的方法。例如,可以提供一个get()方法来获取当前线程的局部变量值,以及一个set()方法来设置当前线程的局部变量值。

  4. 清理线程局部变量:为了避免内存泄漏,需要在不再需要线程局部变量时进行清理。可以在类中提供一个remove()方法,用于清除当前线程的局部变量值。

下面是一个简单的示例代码,演示了如何实现类似于ThreadLocal的功能:

import java.util.HashMap;
import java.util.Map;

public class MyThreadLocal<T> {
    private Map<Long, T> threadLocalMap = new HashMap<>();

    public MyThreadLocal(T initialValue) {
        long threadId = Thread.currentThread().getId();
        threadLocalMap.put(threadId, initialValue);
    }

    public T get() {
        long threadId = Thread.currentThread().getId();
        return threadLocalMap.get(threadId);
    }

    public void set(T value) {
        long threadId = Thread.currentThread().getId();
        threadLocalMap.put(threadId, value);
    }

    public void remove() {
        long threadId = Thread.currentThread().getId();
        threadLocalMap.remove(threadId);
    }
}

上述代码中,MyThreadLocal类使用一个Map来存储每个线程对应的值。通过调用get()、set()和remove()方法,可以方便地获取、设置和清理线程局部变量的值。

需要注意的是,这只是一个简单的示例,实际使用时可能需要根据具体需求进行扩展和优化。此外,还需要考虑线程安全性和性能等因素,以确保实现的正确性和高效性。
简单说就是一个Map,key为对应的线程id,然后存对应的值,每个线程操作的话,都是通过自己线程id来操作的,这就实现了类似ThreadLocal,每个线程都是自己的变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值