ThreadLocal相关

作用和原理

ThreadLocal就是为每一个使用该变量的线程都提供了一个变量值的副本。是Java中一种较为特殊的线程绑定而已,是每一个线程都可以独立的改变自己的副本,而不会跟其他线程的副本发生冲突。
从线程的角度来看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal实例是可以访问的;在线程结束后,其线程的局部实例的所有副本都会被垃圾回收,除非存在对这些副本的引用。
ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM为每个运行的线程绑定了私有的本地变量的存取空间,从而为多线程环境常出现的额并发访问问题提供了一种隔离机制。
实现:
ThreadLocal中有一个map,用于存储每一个线程的变量的副本。
也就是说,对于多线程资源共享问题,同步机制采用了“以时间换空间的方式”,而ThreadLocal采用了“以空间换时间”的方式,前者仅提供一份变量,让不同的变量排队访问。而后者为每一个线程提供了一个副本,因此可以同时访问互不影响。

方法

void set(obj) : 设置当前线程的线程局部变量的值。
public Object get() : 返回房钱线程对应的线程局部变量。
public void remove() : 删除当前线程局部变量的值,减少内存的占用。
线程执行结束之后也会回收对应线程占用的内存空间,所以显示的调用该方法不是必须操作,只是为了加快内存的回收速度。
protected Object initalValue() : 返回该线程局部变量的初始值。该方法是一个延迟调用方法,在县城第一次调用get,set时才执行,并且仅执行一次。

ThreadLocal中的缺省实现直接返回一个null.

package com.cyan.ThreadLocalDamo;
import java.util.Random;
/**
 * @description: 在对象中,获取对象的方法中,先从ThreadLocal中去获取对象,如果对象为空,则创建对象并放进去。
 *                 保证了在线程中获取对象的时候,将创建的对象跟放入到ThreadLocal中,以跟当前线程进行绑定。
 *                 从而保证在此线程中其他位置通过ThreadLocal获取的对象是同一个对象。
 */
public class ThreadLocalDamo2 {
    static ThreadLocal<People> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data = new Random().nextInt();
                    People people = People.getInstance();  //  第一次执行此处代码会创建新的对象并放入ThreadLocal,使对象和当前线程绑定。
                    people.setName("AAA" + data);
                    people.setAge(data);
                    System.out.println(Thread.currentThread().getName() + " Name: " + people.getName() + "  Age: " + people.getAge());
                    new A().get();  //此处获取的对象都是从ThreadLocal中获取对象,所以获取到的是跟当前线程绑定的对象副本
                    new B().get();
                }
            }).start();
        }
    }
    static class A {
        public void get() {
            System.out.println("A: " + Thread.currentThread().getName() + " Name: " + People.getInstance().getName() + "  Age: " + People.getInstance().getAge());
        }
    }
    static class B {
        public void get() {
            System.out.println("B: " + Thread.currentThread().getName() + " Name: " + People.getInstance().getName() + "  Age: " + People.getInstance().getAge());
        }
    }
    static class People {
        private People() {
        }
        public static People getInstance() {
            //从ThreadLocal中获取对象
            People people = threadLocal.get();
            if (people==null) {
                people = new People();
                threadLocal.set(people);
            }
            return people;
        }
        private int age;
        private String name;
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}
模拟实现

ThreadLocal类中有一个map,用于存储每个线程的变量副本,map中元素的键是线程对象,而值对应线程的变量副本。

package com.cyan.ThreadLocalDamo;
/**
 * @description: initialValue()会在某个线程第一次执行get或set的时候调用一次。
 */
public class ThreadLocalDemo3 {
    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
        //此处重写initialValue方法指定每个线程的局部变量的初始值
        public Integer initialValue() {
            return 0;
        }
    };
    public int getNextNum() {
        //线程调用并将当前线程的局部变量进行改变。改变的只有当前线程的局部变量。
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }
    public static void main(String[] args) {
        ThreadLocalDemo3 demo = new ThreadLocalDemo3();
        TestClient t1 = new TestClient(demo);
        TestClient t2 = new TestClient(demo);
        TestClient t3 = new TestClient(demo);
        t1.start();
        t2.start();
        t3.start();
    }
    private static class TestClient extends Thread {
        private ThreadLocalDemo3 demmo;
        public TestClient(ThreadLocalDemo3 demmo) {
            this.demmo = demmo;
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("Thread : " + Thread.currentThread().getName() +
                "  --> Num : " + demmo.getNextNum());
            }
        }
    }
}

以上例子,每个线程产生了各自单独的序列号,虽然共享一个ThreadLocal实例,但是并没有发生任何冲突,这是因为ThreadLocal为每个线程提供了一个单独的副本。

Thread的同步机制的比较

相同点

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突的问题

不同点

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这是该变量是多个线程共享的,使用同步机制要求程序准确的分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放锁等问题。
ThreadLocal会为每个线程提供独立的变量副本,从而隔离多个线程对数据的访问冲突。因为每个线程都有自己的变量副本,所以不需要进行同步。
ThreadLocal可以支持任何类型的对象。在jdk5后加入优化了泛型。

高级货

要做到多个Dao共享同一Conn,必须在共同的外部类使用ThreadLocal保存Conn。
Connection的管理

package com.cyan.ThreadLocalDamo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @description:
 */
public class ConnectionManager {
    private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
        @Override
        protected Connection initialValue() {
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/test",
                    "username",
                    "password"
                );
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
    };
    public static Connection getConn() {
        return connectionHolder.get();
    }
    public static void setConn(Connection conn) {
        connectionHolder.set(conn);
    }
}

Session的管理

public class HibernateUtil {
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    private static final SessionFactory sessionFactory;     //定义SessionFactory
    static {
        try {
            // 通过默认配置文件hibernate.cfg.xml创建SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            log.error("初始化SessionFactory失败!", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    //创建线程局部变量session,用来保存Hibernate的Session
    public static final ThreadLocal session = new ThreadLocal();
    /**
     * 获取当前线程中的Session
     */
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        // 如果Session还没有打开,则新开一个Session
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);         //将新开的Session保存到线程局部变量中
        }
        return s;
    }
    public static void closeSession() throws HibernateException {
        //获取线程局部变量,并强制转换为Session类型
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}

总结

ThreadLocal主要用于结局多线程中数据因并发产生不一致的问题。ThreadLocal为每个线程中并发访问的数据提供了一个副本,通过访问副本来运行业务,这样的结果就是耗费了内存,但是减少了线程同步带来的性能消耗,也减少了并发控制的复杂度。
ThreadLocal不能使用原子类型。??????

ThreadLocal和Synchornized都用于解决多线程并发访问。synchornized是利用锁的机制,确保使变量或代码块在某一时刻只能被一个线程访问,而ThreadLocal为每个线程都提供了变量的副本,使得献策还能够在某一个时刻访问到的不是同一个对象,这样就隔离了多个线程对数据的数据共享。synchornized主要用于多个线程间通信时能够获得共享数据。
synchornized主要用于线程间的数据共享,ThreadLocal主要用于线程间的数据隔离。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值