什么是线程不安全
简单来说就是多线程访问同一个资源,前面的线程修改了资源,但是后面的线程拿到的依旧是以前的资源,造成的一种脏读现象。比如说抢车票,线程A先去查询有1张,同时线程B也去查询也是1张,此时线程A把票取走了,此时已经没有车票了,但是线程B查询出来的还是有一张车票,但是却取不出来。这样的是线程不安全,也就是多线程访问共享资源存在竞争关系,并且有写操作
什么是线程安全
如果代码在多线程状态下运行出来的结果跟单线程运行出来的结果是一样的,就可以说是线程安全的。线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
线程不安全实例
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
add();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
add();
}
});
thread1.start();
thread2.start();
Thread.sleep(100);
num();
}
public static void add(){
for (int j = 0;j<100;j++){
i++;
}
}
public static void num(){
System.out.println(i);
}
如何保证线程安全
1.synchronized关键字
- 作用于方法上,将方法锁住
- 静态方法:锁住的是类
- 非静态方法:锁住的是对象实例,不同实例访问没有影响
- 锁住代码块
- 因为锁住的地方只能单个单个线程的执行所有性能会比较低
2.ReentrantLock锁对象
3.ThreadLocal
它是将访问的资源拷贝一份到线程内部,每次进行操作都是对自己线程内部的资源进行操作,并不会影响共享的资源。我个人理解就相当于线程的局部变量,每个线程的局部变量都是互不影响的,在一开始就把共享资源赋值给局部变量,以后每次都对局部变量进行操作,需要注意每次操作完成,记得调用remove方法,不然容易引起内存泄漏。
3.1 主要方法
- get():得到当前线程局部变量中的值
- set():在当前线程的局部变量中设置指定的值
- remove():删除此线程局部变量的当前线程的值
3.2 赋值多个值
- 一个ThreadLocal只能赋值一个局部变量,如果想要将多个值设置为局部变量需要创建多个ThreadLocal,或者将需要设置的多个值封装成一个对象,进行设置。
3.3 注意事项
- 由于ThreadLocal是创建线程局部变量来控制线程安全的,所有会对内存产生很大的消耗
- 使用完ThreadLocal需要及时remove掉局部变量,不然容易引起内存泄漏,进而导致内存溢出
使用ThreadLocal在子线程中无法获取变量
InheritableThreadLocal:可以在子线程获取父线程中设置的局部变量,用法跟ThreadLocal一样