详述 ThreadLocal

ThreadLocal 为解决多线程并发问题提供了一个很好的方式;多说无益,我们以例子来更直观的看 ThreadLocal 的作用;

import java.util.Random;

public class Test {
	
	private static int data = 0;
	
	public static void main(String[] args) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				data = new Random().nextInt();
				System.out.println(Thread.currentThread().getName()+" has put data "+data);
				new A().get();
				new B().get();
			}
		}).start();
	}
	
	static class A{
		public void get() {
			System.out.println("A from "+Thread.currentThread().getName()+" has get data "+data);
		}
	}
	
	static class B{
		public void get() {
			System.out.println("B from "+Thread.currentThread().getName()+" has get data "+data);
		}
	}
}

执行结果:
Thread-0 has put data 1902290541
A from Thread-0 has get data 1902290541
B from Thread-0 has get data 1902290541

这个例子是一个很简单的线程;在 Test 中有一个静态变量 data;这个 data 是线程的共享数据;线程每启动一次都会为 data 重新赋值一个随机数,然后调用 A,B 类中的 get 方法输出其 data 值;结果如上。

问题来了,一个线程对输出肯定是没有影响的,那多个呢?这里以两个为例进行测试;

import java.util.Random;

public class Test {
	
	private static int data = 0;
	
	public static void main(String[] args) {
		for(int i=1;i<=2;i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()+" has put data "+data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
	
	static class A{
		public void get() {
			System.out.println("A from "+Thread.currentThread().getName()+" has get data "+data);
		}
	}
	
	static class B{
		public void get() {
			System.out.println("B from "+Thread.currentThread().getName()+" has get data "+data);
		}
	}
}

一模一样的线程复杂度;我们同时执行两个,本来我们想得到的结果为:获取两个随机数并分别使用 A,B 类中的 get 方法输出获得的随机数;结果我们执行时发现大多数执行结果如下:

我们发现这里得到的结果并非我们想要的,但为什么会出现这种结果呢?很简单:线程是抢占式的执行方式,当线程的复杂度相同时(如我们的例子),线程抢占执行的机会几乎对等;因此两个线程都是你执行到哪我就执行到哪,我们以2线程(即for循环的第一个线程,1线程同)先执行为例:2线程执行到 data = new Random().nextInt();获取了新的随机数值;又因为1线程和2线程抢占执行的机会对等,因此1线程抢得执行权,也执行了该行代码获得了新的随机数值并覆盖了2线程获得的随机数值,因此在接下来的执行中变有了上图结果的输出;

然后我们换一种方式,我们定义一个 Map 作为共享数据获取随机数值并输出:

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class Test {
	
	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
	
	public static void main(String[] args) {
		for(int i=1;i<=2;i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()+" has put data "+data);
					threadData.put(Thread.currentThread(), data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
	
	static class A{
		public void get() {
			int data = threadData.get(Thread.currentThread());
			System.out.println("A from "+Thread.currentThread().getName()+" has get data "+data);
		}
	}
	
	static class B{
		public void get() {
			int data = threadData.get(Thread.currentThread());
			System.out.println("B from "+Thread.currentThread().getName()+" has get data "+data);
		}
	}
}

执行结果如下:

虽然这种方法可以得到我们想要的结果,但是创建的 Map 是需要人工释放的,如果不释放,大量线程执行,会很快使内存溢出,因此,该方法依旧不可取;

synchronized 方法同理,也需要手动清理数据;这时 ThreadLocal 优势就显得明显了;

ThreadLocal  实现数据共享:

import java.util.Random;

public class Test {
	
	static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
	
	public static void main(String[] args) {
		for(int i=0;i<=1;i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName()+" has put data "+data);
					x.set(data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}

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

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

执行结果如下:

确认过眼神,是我们需要的数据;然后再看其 set 方法:

是采用的 set 方法存储数据,而不是 put 方法;因此即使不人工释放资源也不会造成内存溢出。

发布了99 篇原创文章 · 获赞 3 · 访问量 1204

猜你喜欢

转载自blog.csdn.net/qq_44971038/article/details/104535915