Java并发笔记五——ThreadLocal

线程范围内数据共享

package multiThread;

import java.util.concurrent.ThreadLocalRandom;

public class ThreadScopeSharedData {
    private static int data = 0;

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                public void run() {
                    data = ThreadLocalRandom.current().nextInt();
                    System.out.println(Thread.currentThread().getName() +
                            " has put data " + data);
                    new A().get();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    new B().get();
                }
            }).start();
            Thread.sleep(1000);
        }
    }

    static class A {
        public void get() {
            System.out.println(
                    Thread.currentThread().getName() + " A : " + data);
        }
    }

    static class B {
        public void get() {
            System.out.println(
                    Thread.currentThread().getName() + " B : " + data);
        }
    }
}

/*
Thread-0 has put data -1657334192
Thread-0 A : -1657334192
Thread-1 has put data -2108767251
Thread-1 A : -2108767251
Thread-0 B : -2108767251    <-------
Thread-1 B : -2108767251
*/

加入map,将产生的数据与当前线程绑定后放入map

public class ThreadScopeSharedData {
    private static int data = 0;

    private static Map<Thread, Integer> map = new HashMap<>();
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                public void run() {
                    data = ThreadLocalRandom.current().nextInt();
                    map.put(Thread.currentThread(), data);
                    System.out.println(Thread.currentThread().getName() +
                            " has put data " + data);
                    new A().get();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    new B().get();
                }
            }).start();
            Thread.sleep(1000);
        }
        System.out.println("now the data is " + data);
    }

    static class A {
        public void get() {
            int data = map.get(Thread.currentThread());
            System.out.println(
                    Thread.currentThread().getName() + " A : " + data);
        }
    }

    static class B {
        public void get() {
            int data = map.get(Thread.currentThread());
            System.out.println(
                    Thread.currentThread().getName() + " B : " + data);
        }
    }
}

/*
Thread-0 has put data -563709465
Thread-0 A : -563709465
Thread-1 has put data -393013760
Thread-1 A : -393013760
Thread-0 B : -563709465   <-----------
Thread-1 B : -393013760
now the data is -393013760
*/

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

这种思想用在JavaEE里面的一个场景描述如下:银行转账业务,A,B,C用的是同一个Connection的不同线程开启一个事务,将事务上下文保存在静态的ThreadLocal对象中,可以很容易地实现这个功能。

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalTest {
    private static ThreadLocal<Integer> x = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                public void run() {
                    int data = ThreadLocalRandom.current().nextInt();
                    x.set(data);
                    System.out.println(Thread.currentThread().getName() +
                            " has put data " + data);
                    new A().get();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    new B().get();
                }
            }).start();
            Thread.sleep(1000);
        }
        System.out.println("now the data is " + x.get());
    }

    static class A {
        public void get() {
            int data = x.get();
            System.out.println(
                    Thread.currentThread().getName() + " A : " + data);
        }
    }

    static class B {
        public void get() {
            int data = x.get();
            System.out.println(
                    Thread.currentThread().getName() + " B : " + data);
        }
    }
}

/*
Thread-0 has put data -1154183505
Thread-0 A : -1154183505
Thread-1 has put data 583017524
Thread-1 A : 583017524
Thread-0 B : -1154183505
Thread-1 B : 583017524    <-----------
now the data is null      <-----------主线程没有设定这个变量
*/

一个ThreadLocal只能代表一个变量,放一个数据,如果有多个数据,则需要将多个数据封装成一个对象放入ThreadLocal中。

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalObjectTest {
    private static ThreadLocal<Integer> x = new ThreadLocal<>();
    private static ThreadLocal<MyThreadLocalData> myThreadLocaldata = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                public void run() {
                    int data = ThreadLocalRandom.current().nextInt();
                    x.set(data);
                    System.out.println(Thread.currentThread().getName() +
                            " has put data " + data);
                    MyThreadLocalData myData = new MyThreadLocalData();
                    myData.setName("name" + data + ", age ");
                    myData.setAge(data);
                    myThreadLocaldata.set(myData);
                    new A().get();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    new B().get();
                }
            }).start();
            Thread.sleep(1000);
        }
    }

    static class A {
        public void get() {
            int data = x.get();
            System.out.println(
                    Thread.currentThread().getName() + " A : " + data);
            MyThreadLocalData myData = myThreadLocaldata.get(); 
            System.out.println(
                    Thread.currentThread().getName() + " A : " +  myData.getName() + myData.getAge());
        }
    }

    static class B {
        public void get() {
            int data = x.get();
            System.out.println(
                    Thread.currentThread().getName() + " B : " + data);
            MyThreadLocalData myData = myThreadLocaldata.get(); 
            System.out.println(
                    Thread.currentThread().getName() + " B : " + myData.getName() + myData.getAge());
        }
    }
}

class MyThreadLocalData {
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
/*
Thread-0 has put data -73677040
Thread-0 A : -73677040
Thread-0 A : name-73677040, age -73677040
Thread-1 has put data 128646006
Thread-1 A : 128646006
Thread-1 A : name128646006, age 128646006
Thread-0 B : -73677040
Thread-0 B : name-73677040, age -73677040
Thread-1 B : 128646006
Thread-1 B : name128646006, age 128646006
*/

这样的代码太丑陋,所以对上面的例子程序进行改进。

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalSingleTest {
    private static ThreadLocal<Integer> x = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                public void run() {
                    int data = ThreadLocalRandom.current().nextInt();
                    x.set(data);
                    System.out.println(Thread.currentThread().getName() +
                            " has put data " + data);
                    MyThreadLocalDataSingle.getThreadInstance().setName("name" + data + ", age ");
                    MyThreadLocalDataSingle.getThreadInstance().setAge(data);
                    new A().get();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    new B().get();
                }
            }).start();
            Thread.sleep(1000);
        }
    }

    static class A {
        public void get() {
            int data = x.get();
            System.out.println(
                    Thread.currentThread().getName() + " A : " + data);
            MyThreadLocalDataSingle myData = MyThreadLocalDataSingle.getThreadInstance();
            System.out.println(
                    Thread.currentThread().getName() + " A : " +  myData.getName() + myData.getAge());
        }
    }

    static class B {
        public void get() {
            int data = x.get();
            System.out.println(
                    Thread.currentThread().getName() + " B : " + data);
            MyThreadLocalDataSingle myData = MyThreadLocalDataSingle.getThreadInstance();
            System.out.println(
                    Thread.currentThread().getName() + " B : " + myData.getName() + myData.getAge());
        }
    }
}

class MyThreadLocalDataSingle {
    private static ThreadLocal<MyThreadLocalDataSingle> map = new ThreadLocal<>();

    private MyThreadLocalDataSingle() {}

    // 仅仅和本线程有关,无需加synchronized关键字
    public static /*synchronized*/ MyThreadLocalDataSingle getThreadInstance() {
        MyThreadLocalDataSingle instance = map.get();
        if (instance == null) {
             instance = new MyThreadLocalDataSingle();
             map.set(instance);
        }
        return instance;
    }

    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

/*
Thread-0 has put data -1169633973
Thread-0 A : -1169633973
Thread-0 A : name-1169633973, age -1169633973
Thread-1 has put data 1917450343
Thread-1 A : 1917450343
Thread-1 A : name1917450343, age 1917450343
Thread-0 B : -1169633973
Thread-0 B : name-1169633973, age -1169633973
Thread-1 B : 1917450343
Thread-1 B : name1917450343, age 1917450343
*/

从概念上来看,可以讲ThreadLocal视为包含了Map对象,其中保存了特定于该线程的值,但是实现并非如此。这些特定于线程的值保存在Thread对象中,当线程终止后,这些值会作为垃圾回收。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值