ThreadLocal 基础
多线程的原理概论
开启多线程的方式
- 继承Thread 重写run方法
- 实现接口 runnable 重写run 或者 使用thread的构造方法直接new runnable来重写run
package com.itheima.controller;
public class ThreadDemo {
public static void main(String[] args) {
System.out.println("这是主线程"); //main方法的主线程
Thread thread = new Thread(new Thre()); //new Thread使用实现类开启
Ot ot = new Ot(); //这是继承的方式开启线程
new Thread(new Runnable() { //new Thread使用构造方法开启
@Override
public void run() {
System.out.println("这也是一个线程任务");
}
}).start();
thread.start();
ot.start();
}
}
小结:
开启线程的时候,只认run方法
当new Thread对象的时候 如果该对象的run方法没有被重写,那么就会自动去找runnable重写的run方法来开启线程
互斥与同步
互斥 : A 使用时 B必然等待 因为A在占用(锁)
同步 : A使用时, B等待,因为B要使用A完成之后的数据
等待唤醒机制 await() notify()
案例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8RU8vah-1602380972641)(D:\Java总结文档\图解\多线程\唤醒机制1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-obPZQcqO-1602380972642)(D:\Java总结文档\图解\多线程\唤醒机制2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mlvdciu8-1602380972643)(D:\Java总结文档\图解\多线程\唤醒机制3.png)]
ThreadLocal的方法
方法申明 | 作用 |
---|---|
initialValue() | 返回当前线程局部变量的初始值 |
set() | 设置当前线程绑定的局部变量 |
get() | 获取当前线程绑定的局部变量 |
remove() | 移除当前线程绑定的局部变量 |
ThreadLocal作用
ThreadLocal的作用是 : 在开启多个线程的时候,每个线程要将共享数据作为自己独立的值进行使用,不会被相互访问到,此时需要用到ThreadLocal对每个线程的变量进行隔离。
ThreadLocal 和synchronized的区别
ThreadLocal 和synchronized都可以实现对数据的隔离,但是要分清使用的场景。
ThreadLocal 的主要作用是对共享变量进行隔离,每个线程都有一个自己的变量,此时可以为每个线程都隔离一份仅属于自己的变量,不会出现相互被访问的情况。(一变多)
synchronized的主要作用是为了对某一个共享资源进行封锁,此资源有且仅有一个,若每个线程隔离一个会失去其意义。为了使这个变量从开始到结束时的值都是正确的,不会出现线程安全问题,所以要“锁住“,只能排队访问。(买票等案例)(一不变)
在如下的案例当中,虽然都可以使用并且实现隔离,但是此处使用threadLocal更合适,因为使用synchronized会严重影响系统在高并发时的效率,而此处我不需要对这三条输出语句进行封锁,也不满足有且仅有一个共享资源的前提,我只是需要对每一个线程的content变量进行隔离,所以采用ThreadLocal 更合适。
由两种打印的效果可以得出结论,第二种是在高速切换,第一种是在排队挨个执行
import java.util.*;
public class TTT {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static void main(String[] args) {
TTT test = new TTT();
for(int i = 0 ; i < 5 ; i++){
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
//使用synchronized
synchronized (TTT.class){
test.setContent(Thread.currentThread().getName()+"的数据");
System.out.println("-------------------------------------");
System.out.println(Thread.currentThread().getName()+"---->"+test.getContent());
}
}
});
thread.setName("线程"+i); //为每个线程署名
thread.start(); //开始线程任务
}
}
}
打印效果
-------------------------------------
线程0---->线程0的数据
-------------------------------------
线程4---->线程4的数据
-------------------------------------
线程3---->线程3的数据
-------------------------------------
线程2---->线程2的数据
-------------------------------------
线程1---->线程1的数据
使用thread Local
import java.util.*;
public class TTT {
ThreadLocal<String> threadLocal = new ThreadLocal();
private String content;
public String getContent() {
return threadLocal.get();
}
public void setContent(String content) {
threadLocal.set(content);
}
public static void main(String[] args) {
TTT test = new TTT();
for(int i = 0 ; i < 5 ; i++){
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
test.setContent(Thread.currentThread().getName()+"的数据");
System.out.println("-------------------------------------");
System.out.println(Thread.currentThread().getName()+"---->"+test.getContent());
}
});
thread.setName("线程"+i);
thread.start();
}
}
}
打印效果
-------------------------------------
线程1---->线程1的数据
-------------------------------------
-------------------------------------
线程2---->线程2的数据
-------------------------------------
-------------------------------------
线程4---->线程4的数据
线程3---->线程3的数据
线程0---->线程0的数据
线程2---->线程2的数据
线程4---->线程4的数据
线程3---->线程3的数据
线程0---->线程0的数据