并发状况下如何保证数据安全,一直都是开发人员天天都要面对的问题,稍不注意就会出现数据异常,形成不可挽回的结果。笔者根据本身的实际开发经验,总结了下面几种保证数据安全的技术手段:安全
咱们都知道只有多个线程访问公共资源的时候,才可能出现数据安全问题,那么若是咱们没有公共资源,是否是就没有这个问题呢?多线程
public class NoStatusService { public void add(String status) { System.out.println("add status:" + status); } public void update(String status) { System.out.println("update status:" + status); } }
若是多个线程访问公共资源是不可变的,也不会出现数据的安全性问题。并发
public class NoChangeService { public static final String DEFAULT_NAME = "abc"; public void add(String status) { System.out.println("add status:" + status); } }
若是类中有公共资源,可是没有对外开放访问权限,即对外安全发布,也没有线程安全问题性能
public class SafePublishService { private String name; public String getName() { return name; } public void add(String status) { System.out.println("add status:" + status); } }
若是有些公共资源只是一个开关,只要求可见性,不要求原子性,这样能够用volidate关键字定义来解决问题。this
public class FlagService { public volatile boolean flag = false; public void change() { if (flag) { System.out.println("return"); return; } flag = true; System.out.println("change"); } }
使用JDK内部提供的同步机制,这也是使用比较多的手段,分为:方法同步 和 代码块同步,咱们优先使用代码块同步,由于方法同步的范围更大,更消耗性能。每一个对象内部都又一把锁,只有抢答那把锁的线程,才能进入代码块里,代码块执行完以后,会自动释放锁。编码
public class SyncService { private int age = 1; public synchronized void add(int i) { age = age + i; System.out.println("age:" + age); } public void update(int i) { synchronized (this) { age = age + i; System.out.println("age:" + age); } } }
除了使用synchronized关键字实现同步功能以外,JDK还提供了lock显示锁的方式。它包含:可重入锁、读写锁 等更多更强大的功能,有个小问题就是须要手动释放锁,不过在编码时提供了更多的灵活性。atom
public class LockService { private ReentrantLock reentrantLock = new ReentrantLock(); public int age = 1; public void add(int i) { try { reentrantLock.lock(); age = age + i; System.out.println("age:" + age); } finally { reentrantLock.unlock(); } } }
JDK除了使用锁的机制解决多线程状况下数据安全问题以外,还提供了cas机制。这种机制是使用CPU中比较和交换指令的原子性,JDK里面是经过Unsafe类实现的。cas须要四个值:旧数据、指望数据、新数据 和 地址,比较旧数据 和 指望的数据若是同样的话,就把旧数据改为新数据,当前线程不断自旋,一直到成功为止。不过可能会出现aba问题,须要使用AtomicStampedReference增长版本号解决。其实,实际工做中不多直接使用Unsafe类的,通常用atomic包下面的类便可。线程
public class AtomicService { private AtomicInteger atomicInteger = new AtomicInteger(); public int add(int i) { return atomicInteger.getAndAdd(i); } }
除了上面几种解决思路以外,JDK还提供了另一种用空间换时间的新思路:threadlocal。它的核心思想是:共享变量在每一个线程都有一个副本,每一个线程操做的都是本身的副本,对另外的线程没有影响。特别注意,使用threadlocal时,使用完以后,要记得调用remove方法,否则可能会出现内存泄露问题。code
public class ThreadLocalService { private ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public void add(int i) { Integer integer = threadLocal.get(); threadLocal.set(integer == null ? 0 : integer + i); } }
本文介绍了8种多线程状况下保证数据安全的技术手段,固然实际工做中可能会有其余。技术没有好坏之分,主要是看使用的场景,须要在不一样的场景下使用不一样的技术。对象