Dowemo

A threadlocalvariable isn't a thread, but a local variable of thread, and it may be named more easily.

When a variable is used to maintain a variable, the ds0 provides a separate copy of the variable for each thread using that variable, so each thread can change the

From a thread 's point of view, the target variable is the local variable of the thread, which is the meaning of"local"in the class name.

Threadlocal interface method

A class interface is simple, with only 4 methods, and we're going to know:


void set(Object value)






To set the value of the thread local variable for the current thread.


public Object get()






This method retur & the thread local variable corresponding to the current thread.


public void remove()






Deletes the value of the current thread local variable, and aims to reduce memory footprint, which is a new method for jdk 5.0. It's important to note that when the thread ends, the local variable that should be thread is automatically reclaimed, so explicitly calling the method to clear the local local variable isn't the necessary action, but it speeds up the memory recovery speed.


protected Object initialValue()






Returns the initial value of the local variable, which is a protected method, obviously designed to allow subclasses to override. This method is a delayed invocation method that executes when the thread 1 calls get ( ) or set ( object ), and executes only 1 times. The default implementation in the directly returns a null.

Note that in jdk5. 0, has already supported generics, and the class name of the class has been changed. Api methods are also adjusted, and the new version of the api method is void set ( t value ), t get ( ), and t _ initialvalue ( ).

Ds0 maintenance variables

How does the turtle maintain a copy of the variable for each thread. In fact, the idea is simple: There's a map in the ds0 class to store a copy of each thread 's variable, and the key of the element in the map is a thread object, and a copy of the We can provide a simple version of the implementation:


public class SimpleThreadLocal {


 private Map valueMap = Collections.synchronizedMap(new HashMap());


 public void set(Object newValue) {


 valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本


 }


 public Object get() {


 Thread currentThread = Thread.currentThread();


 Object o = valueMap.get(currentThread);//②返回本线程对应的变量


 if (o == null &&!valueMap.containsKey(currentThread)) {


 //③如果在Map中不存在,放到Map中保存起来。


 o = initialValue();


 valueMap.put(currentThread, o);


 }


 return o;


 }


 public void remove() {


 valueMap.remove(Thread.currentThread());


 }


 public Object initialValue() {


 return null;


 }


}






Although the version of the above code is relatively straightforward, it's similar to that provided by the jdk in the implementation idea.

A theadlocal I & tance

Below, we know how to use a concrete example with a specific example.


public class SequenceNumber {


 //①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值


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


 public Integer initialValue(){


 return 0;


 }


 };


 //②获取下一个序列值


 public int getNextNum(){


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


 return seqNum.get();


 }


 public static void main(String[] args)


 {


 SequenceNumber sn = new SequenceNumber();


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


 TestClient t1 = new TestClient(sn);


 TestClient t2 = new TestClient(sn);


 TestClient t3 = new TestClient(sn);


 t1.start();


 t2.start();


 t3.start();


 }


 private static class TestClient extends Thread


 {


 private SequenceNumber sn;


 public TestClient(SequenceNumber sn) {


 this.sn = sn;


 }


 public void run()


 {


 for (int i = 0; i <3; i++) {//④每个线程打出3个序列值


 System.out.println("thread["+Thread.currentThread().getName()+


 "] sn["+sn.getNextNum()+"]");


 }


 }


}






Typically, we define the subclasses of the ds0 by means of an anonymous inner class, providing the initial variable value, as shown in the example. A testclient thread generates a set of serial numbe & where we generate 3 testclient, which share the same sequencenumber I & tance. Run the above code to output the following results on the co & ole:


thread[Thread-2] sn[1]


thread[Thread-0] sn[1]


thread[Thread-1] sn[1]


thread[Thread-2] sn[2]


thread[Thread-0] sn[2]


thread[Thread-1] sn[2]


thread[Thread-2] sn[3]


thread[Thread-0] sn[3]


thread[Thread-1] sn[3]






Look at the results of the output, we found that each thread generated the sequence number, but they didn't interfere with each other, but they generated a separate serial number, because we provided a separate copy of each thread by the mat, because we provided a separate copy for each thread.

The role of the threadlocal.

Threadlocal isn't used to solve access problems for shared objects, typically by the object that's used by the threadlocal ( ) to the thread in the thread, and other threads aren't required to access it and are accessed by other threads. There are different objects in each thread to ask. ( note that this is just a"general situation". If the object in a threadlocal is shared with the same object in the thread, the access to the same shared object is the same shared object.

1. Provides methods for saving objects: Each thread has its own ThreadLocalMap class object, which allows you to keep its own objects in one of them, each of which can be properly accessed by the thread.

2. The convenient object access method to avoid paramete &: A common threadlocal I tance as key, saving references to different objects to ThreadLocalMap of different threads, and then executing that object at the end of the thread through the get ( ) method of the, avoids the trouble of passing this object as a parameter, avoiding the trouble of passing this object as a parameter.

Unde & tanding the copy of the variables mentioned in the ds0

"when a variable is used to maintain a variable, -- provides a separate copy of the variable for each thread that uses that variable,"isn't implemented through the mixin. Set ( ), but each thread uses the"new object"( or copy ) action to create a copy of the object, so each thread is taken out of its own map, so that the is used as the key for the map ( which is used as the key for map ). (.

If the object in the get. Set ( ) is the same object that's shared by multithreaded, then. ( ) gets or that the shared object itself is --, or there's a concurrent access problem.


/* 


 * 如果ThreadLocal.set()进去的是一个多线程共享对象,那么Thread.get()获取的还是这个共享对象本身-----并不是该共享对象的副本。 


 * 假如:其中其中一个线程对这个共享对象内容作了修改,那么将会反映到其它线程获取的共享对象中----所以说 ThreadLocal还是有并发访问问题的! 


 */ 


public class Test implements Runnable 


{ 


 private ThreadLocal<Person> threadLocal = new ThreadLocal<Person>(); 



 private Person person; 



 public Test(Person person) 


 { 


 this.person = person; 


 } 



 public static void main(String[] args) throws InterruptedException 


 { 


 //多线程共享的对象 


 Person sharePerson = new Person(110,"Sone"); 


 Test test = new Test(sharePerson); 



 System.out.println("sharePerson原始内容:"+sharePerson); 



 Thread th = new Thread(test); 


 th.start(); 


 th.join(); 



 //通过ThreadLocal获取对象 


 Person localPerson = test.getPerson(); 



 System.out.println("判断localPerson与sharePerson的引用是否一致:"+(localPerson==localPerson)); 


 System.out.println("sharePerson被改动之后的内容:"+sharePerson); 


 } 



 @Override 


 public void run() 


 { 


 String threadName = Thread.currentThread().getName(); 


 System.out.println(threadName+":Get a copy of the variable and change!!!"); 


 Person p = getPerson(); 


 p.setId(741741); 


 p.setName("Boy"); 


 } 



 public Person getPerson(){ 


 Person p = (Person)threadLocal.get(); 


 if (p==null) 


 { 


 p= this.person; 


 //set():进去的是多线程共享的对象 


 threadLocal.set(p); 


 } 


 return p; 


 }






General steps used by

1, in classes, such as the ThreadDemo class, create a ds0 object threadxxx that holds the object that requires isolation between threads.

2, in the ThreadDemo class, create a method getXXX ( ) that gets the data to be isolated. ( ) is determined in the method, and if the mixin object is null, the new ( ) an object that isolated the access type should be used and cast to the type to apply.

3, in the run ( ) method of the ThreadDemo class, get the data to operate through the getXXX ( ) method, which guarantees that each thread corresponds to a data object.


/**


 * 学生


 */


public class Student {


 private int age = 0; //年龄



 public int getAge() {


 return this.age;


 }



 public void setAge(int age) {


 this.age = age;


 }


}



/**


 * 多线程下测试程序


 */


public class ThreadLocalDemo implements Runnable {


 //创建线程局部变量studentLocal,在后面你会发现用来保存Student对象


 private final static ThreadLocal studentLocal = new ThreadLocal();



 public static void main(String[] agrs) {


 ThreadLocalDemo td = new ThreadLocalDemo();


 Thread t1 = new Thread(td, "a");


 Thread t2 = new Thread(td, "b");


 t1.start();


 t2.start();


 }



 public void run() {


 accessStudent();


 }



 /**


 * 示例业务方法,用来测试


 */


 public void accessStudent() {


 //获取当前线程的名字


 String currentThreadName = Thread.currentThread().getName();


 System.out.println(currentThreadName + " is running!");


 //产生一个随机数并打印


 Random random = new Random();


 int age = random.nextInt(100);


 System.out.println("thread" + currentThreadName + " set age to:" + age);


 //获取一个Student对象,并将随机数年龄插入到对象属性中


 Student student = getStudent();


 student.setAge(age);


 System.out.println("thread" + currentThreadName + " first read age is:" + student.getAge());


 try {


 Thread.sleep(500);


 }


 catch (InterruptedException ex) {


 ex.printStackTrace();


 }


 System.out.println("thread" + currentThreadName + " second read age is:" + student.getAge());


 }



 protected Student getStudent() {


 //获取本地线程变量并强制转换为Student类型


 Student student = (Student) studentLocal.get();


 //线程首次执行此方法的时候,studentLocal.get()肯定为null


 if (student == null) {


 //创建一个Student对象,并保存到本地线程变量studentLocal中


 student = new Student();


 studentLocal.set(student);


 }


 return student;


 }


}






Run results:


a is running! 


thread a set age to:76 


b is running! 


thread b set age to:27 


thread a first read age is:76 


thread b first read age is:27 


thread a second read age is:76 


thread b second read age is:27






You can see a, b, two thread age is identical at different times at different times. This program can't only realize concurrent concurrency, but also keep the data security.

Here's an example of the following: Simulate a game, randomly set an integer for [ 1, 10 ], then each player to guess the number, each player knows the result of another player, and see who.

This game is really boring, but it's just possible to take each player as a thread, and then use ds0 to record the history of the player 's guess so that it's easy to unde.

Judge: used to set the target numbers and judge the results.

Player: per player as a thread, multiple player parallel to try guess, thread termination when guessing.

Class attempt: classes with mixin fields and guessing action static methods that are used to save a guessing number.

Record: save history data structure with a list field.

In order to be compatible with various types of data, the actual content is an object that passes through set and get, as shown in attempt 's getrecord ( ).

At run time, each player thread is to call the attemp. Guess ( ) method, and then work with the same mixin variable history, but can save each thread 's own data, which is the role of the cla.


public class ThreadLocalTest {



 public static void main(String[] args) {


 Judge.prepare();


 new Player(1).start();


 new Player(2).start();


 new Player(3).start();


 }



}



class Judge {



 public static int MAX_VALUE = 10;


 private static int targetValue;



 public static void prepare() {


 Random random = new Random();


 targetValue = random.nextInt(MAX_VALUE) + 1;


 }



 public static boolean judge(int value) {


 return value == targetValue;


 }



}



class Player extends Thread {



 private int playerId;



 public Player(int playerId) {


 this.playerId = playerId;


 }



 @Override


 public void run() {


 boolean success = false;


 while(!success) {


 int value = Attempt.guess(Judge.MAX_VALUE);


 success = Judge.judge(value);


 System.out.println(String.format("Plyaer %s Attempts %s and %s", playerId, value, success? " Success" : "Failed"));


 }


 Attempt.review(String.format("[IFNO] Plyaer %s Completed by", playerId));


 }



}



class Attempt {



 private static ThreadLocal<Record> history = new ThreadLocal<Record>();



 public static int guess(int maxValue) {


 Record record = getRecord();


 Random random = new Random();


 int value = 0;


 do {


 value = random.nextInt(maxValue) + 1;


 } while (record.contains(value));


 record.save(value);


 return value;


 }



 public static void review(String info) {


 System.out.println(info + getRecord());


 }



 private static Record getRecord() {


 Record record = history.get();


 if(record == null) {


 record = new Record();


 history.set(record);


 }


 return record;


 }



}



class Record {



 private List<Integer> attemptList = new ArrayList<Integer>();;



 public void save(int value) {


 attemptList.add(value);


 }



 public boolean contains(int value) {


 return attemptList.contains(value);


 }



 @Override


 public String toString() {


 StringBuffer buffer = new StringBuffer();


 buffer.append(attemptList.size() + " Times:");


 int count = 1;


 for(Integer attempt : attemptList) {


 buffer.append(attempt);


 if(count <attemptList.size()) {


 buffer.append(",");


 count++;


 }


 }


 return buffer.toString();


 }



}






Run results


Plyaer 2 Attempts 8 and Failed


Plyaer 3 Attempts 6 and Failed


Plyaer 1 Attempts 5 and Failed


Plyaer 2 Attempts 7 and Success


Plyaer 3 Attempts 9 and Failed


Plyaer 1 Attempts 9 and Failed


Plyaer 3 Attempts 2 and Failed


Plyaer 1 Attempts 2 and Failed


[IFNO] Plyaer 2 Completed by 2 Times: 8, 7


Plyaer 3 Attempts 4 and Failed


Plyaer 1 Attempts 1 and Failed


Plyaer 3 Attempts 5 and Failed


Plyaer 1 Attempts 3 and Failed


Plyaer 3 Attempts 1 and Failed


Plyaer 1 Attempts 10 and Failed


Plyaer 3 Attempts 8 and Failed


Plyaer 1 Attempts 6 and Failed


Plyaer 3 Attempts 7 and Success


Plyaer 1 Attempts 4 and Failed


[IFNO] Plyaer 3 Completed by 8 Times: 6, 9, 2, 4, 5, 1, 8, 7


Plyaer 1 Attempts 7 and Success


[IFNO] Plyaer 1 Completed by 9 Times: 5, 9, 2, 1, 3, 10, 6, 4, 7






Hreadlocal get ( )

For the principle of ds0, it can be seen from its get ( ) method.


public class ThreadLocal<T> {


 ...


 public T get() {


 Thread t = Thread.currentThread();


 ThreadLocalMap map = getMap(t);


 if (map!= null) {


 ThreadLocalMap.Entry e = map.getEntry(this);


 if (e!= null)


 return (T)e.value;


 }


 return setInitialValue();


 }



 ThreadLocalMap getMap(Thread t) {


 return t.threadLocals;


 }


 ...


}






Execute get ( ) first, get the current thread, get the ThreadLocalMap - t, threadlocals in thread, and take the actual value as its own key. As you can see, the variable 's variable actually remains in thread, and the container is a map, which is used by thread, and how many entry.




Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs