什么时候会发生线程安全问题
1、多线程并发
2、有共享数据
3、共享数据有修改行为
满足三个条件后,会存在线程安全问题
如何解决线程安全问题
线程同步机制:使用排队执行解决线程安全问题,即线程排队执行,不能并发,会牺牲一部分效率
同步编程模型:
线程t1和线程t2,在线程1执行的时候必须等待线程2执行结束,线程2执行的时候必须等待线程1结束,两个线程之间发生了等待关系,就是线程同步的思想 ,这种情况效率较低
异步编程模型
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,这种是多线程并发,效率较高
总的来说,异步就是并发,同步就是排队
线程同步synchronized:
synchronized():属于一种排它锁
synchronized三种写法
1、同步代码块
优点:灵活
synchronized(线程共享对象){
//同步代码块
}
2、在实例方法上使用synchronized
表示共享对象一定是this
并且同步代码块是整个方法体
3、 在静态方法上使用synchronized
表示找类锁;类锁永远只有一把,即便这个类创建了100个对象,类锁也只有一把。
对象锁和类锁
对象锁:1个对象一把锁
类型:永远只有一把锁
synchronized最后不要嵌套使用,容易造成死锁情况
死锁:
现象:程序不会出现异常,不会出现错误,程序一直僵持。
死锁代码
package com.bjpowernode.java.deadlock;
/*
死锁代码
*/
public class DeadLock {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
// t1和t2两个线程共享o1,o2
Thread t1 = new MyThread1(o1,o2);
Thread t2 = new MyThread2(o1,o2);
t1.start();
t2.start();
}
}
class MyThread1 extends Thread{
Object o1;
Object o2;
public MyThread1(Object o1,Object o2){
this.o1 = o1;
this.o2 = o2;
}
public void run(){
synchronized (o1){
try {
//让这个线程睡眠一秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class MyThread2 extends Thread {
Object o1;
Object o2;
public MyThread2(Object o1,Object o2){
this.o1 = o1;
this.o2 = o2;
}
public void run(){
synchronized (o2){
try {
//让线程睡眠一秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
在开发中如何解决线程安全问题
1、尽量使用局部变量代替实例变量和静态变量
2、如果必须是实例变量,那么可以考虑创建多个对象,这种方式实例变量的内存就不共享了(一个线程对应一个对象,多个线程对应多个对象,对象不共享就没有数据安全问题)
3、如果不能使用局部变量,对象也不能创建多个,就只能选择synchronized,线程同步机制