多线程与高并发11-并发安全

线程安全

  • 栈封闭:把对象封装在一个线程里,只有这个线程才能看到,无全局变量
  • 无状态:没有任何成员变量的类,就叫无状态的类,这种类一定线程安全
  • 让类不可变:类中的成员变量加上final关键字,不提供修改成员变量的地方,但是成员变量中如果有对象,final关键字也不能保证类的线程安全
  • volatile:不能保证线程安全性,只能保证线程间的可见性(缓存一致性协议)和防止指令重排序(内存屏障,读屏障、写屏障
  • 加锁和CAS:最常用的线程安全手段,synchronized关键字,显示锁,使用各种原子变量,修改数据时使用无锁优化CAS机制
  • 安全的发布:类的成员变量对外暴露,如果是基本类型,发布出去的是变量的一个副本,没有问题,如果是对象就要当心
  • TheadLocal:ThreadLocal是实现线程封闭的最好方法。ThreadLocal内部维护了一个MapMapkey是每个线程的名称,而Map的值就是我们要封闭的对象。每个线程中的对象都对应着Map中一个值,也就是ThreadLocal利用Map实现了对象的线程封闭
Servlet是否线程安全
不是线程安全的类,为什么我们平时没感觉到:
1、在需求上,很少有共享的需求
2、接收到了请求,返回应答的时候,一般都是由一个线程来负责的
但是只要Servlet中有成员变量,一旦有多线程下的写,就很容易产生线程安全问题

死锁

示例代码:

/**
 *类说明:演示普通账户的死锁和解决
 */
public class NormalDeadLock {
    private static Object valueFirst = new Object();//第一个锁
    private static Object valueSecond = new Object();//第二个锁

    //先拿第一个锁,再拿第二个锁
    private static void fisrtToSecond() throws InterruptedException {
        synchronized (valueFirst){
            synchronized (valueSecond){
            }
        }
    }

    //先拿第二个锁,再拿第一个锁
    private static void SecondToFisrt() throws InterruptedException {
        synchronized (valueFirst){
            synchronized (valueSecond){
            }
        }
    }

    private static class TestThread extends Thread{

        private String name;

        public TestThread(String name) {
            this.name = name;
        }

        public void run(){
            Thread.currentThread().setName(name);
            try {
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

解决

定位

要解决死锁,当然要先找到死锁,怎么找?

通过jps 查询应用的 id,再通过jstack id 查看应用的锁的持有情况

修改

保证那锁的顺序一致,或者如果连个锁之间是有联系的,那么就合起来定义为一把锁

线程安全的单例模式

懒汉式-双重检查
/**
 * 懒汉式-双重检查
 */
public class SingleDcl {
    private volatile static SingleDcl singleDcl;
    //私有化
    private SingleDcl(){
    }

    public static SingleDcl getInstance(){
        if (singleDcl == null){ //第一次检查,不加锁
            System.out.println(Thread.currentThread()+" is null");
            synchronized(SingleDcl.class){ //加锁
                if (singleDcl == null){ //第二次检查,加锁情况下
                    System.out.println(Thread.currentThread()+" is null");
                    //内存中分配空间  1
                    //空间初始化 2
                    //把这个空间的地址给我们的引用  3
                    singleDcl = new SingleDcl();
                }
            }
        }
        return singleDcl;
    }
}

注意:

示例对象Instence,一定要定义为volatile,防止指令重排序
饿汉式
/**
 * 饿汉式
 */
public class SingleEHan {
    private SingleEHan(){}
    private static SingleEHan singleDcl = new SingleEHan();

}

注意:

饿汉式最为简单,也不容易错
懒汉式-延迟初始化占位类模式
/**
 * 懒汉式-延迟初始化占位类模式
 */
public class SingleInit {
    private SingleInit(){}

    private static class InstanceHolder{
        private static SingleInit instance = new SingleInit();
    }

    public static SingleInit getInstance(){
        return InstanceHolder.instance;
    }

}

注意:

静态内部类由JVM保证了初始化时的单例和线程安全

你可能感兴趣的:(volatile)