ThreadLocal的直观浅显理解

来源:互联网 发布:淘宝退货订单号填错 编辑:程序博客网 时间:2024/05/16 17:01

在java中,如果某个对象是非线程安全的共享资源,在多线程环境下,如果不加任何限制,对该资源的访问会存在冲突问题。针对这个问题,有以下几种解决方案:

1.加synchronized关键字,这种做法会限制并发访问,影响效率;

2.使用ThreadLocal维护共享资源(变量),为每个使用共享资源(变量)的线程提供独立的变量副本,从而避免冲突问题。

下面先来看一个例子

 * 

*序列号产生类

 */

public class SequenceNumber {

    //序列号变量

    private int segNum=0;

    //获取下一个序列值

    public int getNextNum(){

        return ++segNum;

    }

}

/**

 * 

序列号产生线程   

 */

public class TestClient extends Thread{

    

    //序列号产生类对象

    private SequenceNumber sn;

    //构造函数

    public TestClient(String name,SequenceNumber sn) {

        super(name);

        this.sn = sn;

    }

    public void run() {

        for (int i = 0; i < 3; i++) {

            //每个线程打出3个序列值

            System.out.println(getName()+":sn--->" + sn.getNextNum());

        }

    }

}

/**

 * 

主方法类

 */

public class Main {

    public static void main(String[] args) {

        SequenceNumber sn = new SequenceNumber();

        // 3个线程共享sn,各自产生序列号

        TestClient t1 = new TestClient("A",sn);

        TestClient t2 = new TestClient("B",sn);

        TestClient t3 = new TestClient("C",sn);

        t1.start();

        t2.start();

        t3.start();

    }

}

运行主方法类,输出

A:sn--->1

A:sn--->2

A:sn--->3

B:sn--->4

B:sn--->5

B:sn--->6

C:sn--->7

C:sn--->8

C:sn--->9

下面修改一下SequenceNumber类的实现

*序列号产生类

 */

public class SequenceNumber {

    //序列号变量,通过匿名内部类覆盖ThreadLocalinitialValue()方法,指定初始值

    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {

        public Integer initialValue() {

            return 0;

        }

    };

    //获取下一个序列值

    public int getNextNum() {

        seqNum.set(seqNum.get() + 1);

        return seqNum.get();

    }

}

运行主方法类,输出

A:sn--->1

A:sn--->2

A:sn--->3

C:sn--->1

B:sn--->1

B:sn--->2

B:sn--->3

C:sn--->2

C:sn--->3

可见,将SequenceNumber类的seqNum变量由int类型换成自定义的ThreadLocal<Integer>类型后,各个线程之间操作该共享对象的变量就互不影响了。

具体原因可以看下ThreadLocal的源代码。

原创粉丝点击