什么是线程安全?
多个线程同时操作(读写 写写 )共享资源时,导致共享资源被线程不确定的修改读取
线程安全问题会导致什么样的结果?
竞态条件(Race condition):多个线程同时修改同一个变量或数据结构,导致结果与预期不符。
临界区(Critical section):多个线程同时访问同一个共享资源,导致数据不一致或程序崩溃。
死锁(Deadlock):多个线程互相等待对方释放锁,导致程序无法继续执行。
为了保证线程安全,可以采取哪些操作?
使用同步机制(Synchronization):例如互斥锁、条件变量、信号量等,可以保证多个线程对共享数据的访问和修改是互斥的。
使用不可变对象(Immutable object):即创建一个对象后,其状态不可改变。由于不可变对象的状态不会发生变化,所以多个线程同时访问该对象时不会出现竞态条件。
使用线程局部存储(Thread-local storage):即为每个线程分配独立的存储空间,从而避免多个线程之间的数据冲突和同步问题。
避免共享数据:即尽可能避免多个线程之间对同一个共享数据的访问,而是将共享数据拆分为多个独立的部分,每个部分只由一个线程访问和修改。
Volatile能否保证线程安全?
public class Manage{
static volatile boolean flag1 = true;
new Thread (() -> {
System.out.println ("ThreadA - start" + flag1);
while(flag1){
// System.out.print ("");
}
System.out.println ("ThreadA - stop" + flag1);
}).start ();
try {
Thread.sleep (2000);
} catch (InterruptedException e) {
throw new RuntimeException (e);
}
new Thread (() -> {
System.out.println ("ThreadB start");
flag1 = false;
System.out.println ("ThreadB stop -- " + flag1);
}).start ();
以上程序运行后,当线程A启动后,线程B启动并将flag1赋值为false,如果不加volatile修饰flag1,线程A中的flag1仍然是true,volatile的作用是保持flag1的可见性,但volatile不能保证线程安全。
可见性是指一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就是这个操作同样存在线程安全问题。
new Thread (){
@Override
public void run(){
synchronized (objA) {
System.out.println ("持有A锁");
try {
Thread.sleep (2000);
} catch (InterruptedException e) {
throw new RuntimeException (e);
}
synchronized (objB) {
objB.notifyAll ();
System.out.println ("执行");
}
}
System.out.println ("解锁");
}
}.start ();
new Thread (){
@Override
public void run(){
try {
Thread.sleep (1000);
} catch (InterruptedException e) {
throw new RuntimeException (e);
}
try {
System.out.println ("持有B锁");
synchronized (objB) {
objB.wait ();// 释放锁
synchronized (objA) {
System.out.println ("2执行");
}
}
} catch (InterruptedException e) {
throw new RuntimeException (e);
} finally {
System.out.println ("objB wait....");
synchronized (objB) {
synchronized (objA) {
System.out.println ("2执行");
}
}
}
System.out.println ("2解锁");
}
}.start ();
}
static Object objA = new Object ();
static Object objB = new Object ();
}
双循环锁很容易出现死锁问题,此时利用wait()方法和notifyAll()方法让线程2等待,释放锁,让线程1得到objB的资源,notifyAll()方法是唤醒当前对象上的等待线程;notify()是唤醒单个线程,notifyAll()是唤醒所有的线程,唤醒线程2,让线程2得以启动。