线程封闭之ThreadLocal和栈封闭

3 篇文章 1 订阅
3 篇文章 0 订阅

                                                   线程封闭

当访问共享数据时,通常是要使用同步。如果要避免使用同步,就是不提供共享数据。如果仅在单线程中访问数据,就不需要同步,这种技术就叫做线程封闭,它是实现线程安全最简单的方式之一。当某个对象封闭在一个线程当中时将自动实现线程安全性,即使被封闭的对象本身它并不是安全的,实现线程主要有三种方式。

 

Ad-hoc线程封闭

 

Ad-hoc是指维护线程封闭是由程序自己去实现和维护。Ad-hoc非常脆弱,因为它没有一种语言特性,例如可见性修饰符或局部变量,能将对象封闭到特定的线程上。

 

   栈封闭

 

栈封闭简单理解就是通过局部变量来实现线程封闭,多个线程访问对象的同一个方法,方法内部的局部变量会拷贝到每个线程的线程栈当中,只有当前线程才能访问到,互不干扰。所以局部变量是不被多个线程所共享的。

示例:

public class ThreadTest {
    private int num;
    public void test(int key){
        int flag=0;
        for(int i=0;i<key;i++){
            flag=flag+1;
            num=num+1;
        }
        System.out.println(Thread.currentThread().getName()+"num"+num);
        System.out.println(Thread.currentThread().getName()+"flag"+flag);
    }

    public static void main(String[] args) {

        ThreadTest threadTest=new ThreadTest();
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                threadTest.test(10);
            }
        });
        thread1.start();
        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                threadTest.test(9);
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
}

# 结果
Thread-0num10
Thread-0flag10
Thread-1num19
Thread-1flag9

由示例可以看出,在类上有一个全局变量num,方法里有一个局部变量flag,启动两个线程去调用这个方法,发现num的结果是两个线程结果之和,而flag是互不干扰,所以局部变量是不被多个线程所共享的。

 

 ThreadLocal

 

维护线程封闭一种更规范的方法就是使用ThreadLocal。ThreadLocal提供get和set方法,这些方法为每个使用该变量的线程都存有一份独立的副本因此get总是返回的是当前线程在调用set时设置的值。

 

ThreadLocal一般用于防止对可变的单实例变量或者全局变量进行共享。例如在单线程应用程序中可能会维持一个全局的数据库连接,并在程序启动的时候初始化这个对象,从而避免在调用每个方法都要传递一个Connection对象。由于JDBC的连接对象不一定是线程安全的,因此,当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的,通过将JDBC的连接保存到ThreadLocal中,每个线程都会拥有属于自己的连接。

ThreadLocal代码示例:

/** 线程封闭示例 */
public class Demo7 {
    /** threadLocal变量,每个线程都有一个副本,互不干扰 */
    public static ThreadLocal<String> value = new ThreadLocal<>();

    /**
     * threadlocal测试
     *
     * @throws Exception
     */
    public void threadLocalTest() throws Exception {

        // threadlocal线程封闭示例
        value.set("这是主线程设置的123"); // 主线程设置值
        String v = value.get();
        System.out.println("线程1执行之前,主线程取到的值:" + v);

        new Thread(new Runnable() {
            @Override
            public void run() {
                String v = value.get();
                System.out.println("线程1取到的值:" + v);
                // 设置 threadLocal
                value.set("这是线程1设置的456");

                v = value.get();
                System.out.println("重新设置之后,线程1取到的值:" + v);
                System.out.println("线程1执行结束");
            }
        }).start();
        Thread.sleep(5000L); // 等待所有线程执行结束
        v = value.get();
        System.out.println("线程1执行之后,主线程取到的值:" + v);

    }

    public static void main(String[] args) throws Exception {
        new Demo7().threadLocalTest();
    }
}
# 结果
线程1执行之前,主线程取到的值:这是主线程设置的123
线程1取到的值:null
重新设置之后,线程1取到的值:这是线程1设置的456
线程1执行结束
线程1执行之后,主线程取到的值:这是主线程设置的123

通过代码示例,可以看到主线程和子线程的执行结果互不干扰。当某个频繁执行的操作需要一个临时对象,例如一个缓冲区,而同时又希望避免在每次执行时都重新分配该临时对象,就可以使用ThreadLocal。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员蛋蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值