多线程访问临界资源时的数据安全问题
临界资源 :多个线程同时访问的资源。
产生原因:有多个线程在同时访问一个资源,如果一个线程在取值的过程中,时间片又被其他线程抢走了,临界资源问题就产生了
演示:卖票案例
同步代码块
同步:Synchronized:有等待
异步:Asynchronized:没有等待,各执行各的
语法:
synchronized(锁) {
//需要访问临界资源的代码段
}
说明:
a.程序走到代码段中,就用锁来锁住了临界资源,这个时候,其他线程不能执行代码段中的代码,只能在锁外边等待
b.执行完代码段中的这段代码,会自动解锁。然后剩下的其他线程开始争抢cpu时间片
c.一定要保证不同的线程看到的是同一把锁,否则同步代码块没有意义
ReentrantLock类(可重入锁)jdk1.5
从jdk1.5之后加入新的接口 Lock,ReentrantLock是Lock接口的实现类。
通过显式定义同步锁对象来实现同步,同步锁提供了比synchronized代码块更广泛的锁定操
注意:最好将 unlock的操作放到finally块中
通过使用ReentrantLock这个类来进行锁的操作,它实现了Lock接口,使用ReentrantLock可以显式地加锁、释放锁
死锁
每个人都拥有其他人需要的资源,同时又等待其他人拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源。
当多个线程完成功能需要同时获取多个共享资源的时候可能会导致死锁。
死锁的条件:
1两个以上的线程
2至少两个锁以上
3同步中嵌套同步
多线程在单例中的应用
单例的实现方式:懒汉式和饿汉式
其中,懒汉式是线程不安全的,当有多条线程同时访问单例对象时,则会出现多线程临界资源问题
单例实现步骤:
1 私有化构造方法
2 在类中创建对象
3 通过公开的方法返回这个对象
volatile关键字的作用:
(1)保证线程可见性
(2)禁止指令重排序
什么是指令重排序?
第一种写法:
// 懒汉式
//1 私有化构造方法
// 2 在类中创建对象
// 3 通过公开的方法返回这个对象
public class SingleTon {
private SingleTon() {
//禁止反射破解
synchronized (SingleTon.class) {
if (instance != null) {
throw new RuntimeException(“不能使用反射创建对象”);
}
}
}
private static volatile SingleTon instance = null;//volatile:不稳定的,易挥发的
public static SingleTon getInstance() {
if (instance == null) {//double check.提高效率
synchronized (SingleTon.class) {
if (instance == null) {
instance = new SingleTon();
}
}
}
return instance;
}
}
第二种写法:
/*
-
静态内部类写法
-
(1)节省空间
-
(2)不会线程安全问题
*/
public class SingleTon2 {
private SingleTon2(){
}static class Holder{
private static final SingleTon2 INSTACNE=new SingleTon2();
}public static SingleTon2 getInstance(){
return Holder.INSTACNE;
}
}
第三种写法
// 枚举写法
//(1)没有线程安全问题
//(2)解决反射破解问题
public enum SingleTon3 {
INSTANCE;
public static SingleTon3 getInstance(){
return INSTANCE;
}
}
Condition接口
通常和Lock连用,该接口可以创建不同的等待队列。
private Lock lock = new ReentrantLock();
Condition pro_condition = lock.newCondition();//生产队列
Condition con_condition = lock.newCondition();//消费队列
void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。
boolean await(long time, TimeUnit unit) 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout) 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
void awaitUninterruptibly() 造成当前线程在接到信号之前一直处于等待状态。
boolean awaitUntil(Date deadline) 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
void signal() 唤醒一个等待线程。
void signalAll() 唤醒所有等待线程。