精华内容
下载资源
问答
  • SimpleDateFormat不是线程安全的根本原因和解决方案,冰河吐血整理,建议收藏!!

    大家好,我是冰河~~

    首先问下大家:你使用的SimpleDateFormat类还安全吗?为什么说SimpleDateFormat类不是线程安全的?带着问题从本文中寻求答案。

    提起SimpleDateFormat类,想必做过Java开发的童鞋都不会感到陌生。没错,它就是Java中提供的日期时间的转化类。这里,为什么说SimpleDateFormat类有线程安全问题呢?有些小伙伴可能会提出疑问:我们生产环境上一直在使用SimpleDateFormat类来解析和格式化日期和时间类型的数据,一直都没有问题啊!我的回答是:没错,那是因为你们的系统达不到SimpleDateFormat类出现问题的并发量,也就是说你们的系统没啥负载!

    接下来,我们就一起看下在高并发下SimpleDateFormat类为何会出现安全问题,以及如何解决SimpleDateFormat类的安全问题。

    重现SimpleDateFormat类的线程安全问题

    为了重现SimpleDateFormat类的线程安全问题,一种比较简单的方式就是使用线程池结合Java并发包中的CountDownLatch类和Semaphore类来重现线程安全问题。

    有关CountDownLatch类和Semaphore类的具体用法和底层原理与源码解析在【高并发专题】后文会深度分析。这里,大家只需要知道CountDownLatch类可以使一个线程等待其他线程各自执行完毕后再执行。而Semaphore类可以理解为一个计数信号量,必须由获取它的线程释放,经常用来限制访问某些资源的线程数量,例如限流等。

    好了,先来看下重现SimpleDateFormat类的线程安全问题的代码,如下所示。

    package io.binghe.concurrent.lab06;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 测试SimpleDateFormat的线程不安全问题
     */
    public class SimpleDateFormatTest01 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
        //SimpleDateFormat对象
        private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            simpleDateFormat.parse("2020-01-01");
                        } catch (ParseException e) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }catch (NumberFormatException e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    可以看到,在SimpleDateFormatTest01类中,首先定义了两个常量,一个是程序执行的总次数,一个是同时运行的线程数量。程序中结合线程池和CountDownLatch类与Semaphore类来模拟高并发的业务场景。其中,有关日期转化的代码只有如下一行。

    simpleDateFormat.parse("2020-01-01");
    

    当程序捕获到异常时,打印相关的信息,并退出整个程序的运行。当程序正确运行后,会打印“所有线程格式化日期成功”。

    运行程序输出的结果信息如下所示。

    Exception in thread "pool-1-thread-4" Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-2" 线程:pool-1-thread-7 格式化日期失败
    线程:pool-1-thread-9 格式化日期失败
    线程:pool-1-thread-10 格式化日期失败
    Exception in thread "pool-1-thread-3" Exception in thread "pool-1-thread-5" Exception in thread "pool-1-thread-6" 线程:pool-1-thread-15 格式化日期失败
    线程:pool-1-thread-21 格式化日期失败
    Exception in thread "pool-1-thread-23" 线程:pool-1-thread-16 格式化日期失败
    线程:pool-1-thread-11 格式化日期失败
    java.lang.ArrayIndexOutOfBoundsException
    线程:pool-1-thread-27 格式化日期失败
    	at java.lang.System.arraycopy(Native Method)
    	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:597)
    	at java.lang.StringBuffer.append(StringBuffer.java:367)
    	at java.text.DigitList.getLong(DigitList.java:191)线程:pool-1-thread-25 格式化日期失败
    
    	at java.text.DecimalFormat.parse(DecimalFormat.java:2084)
    	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
    	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    线程:pool-1-thread-14 格式化日期失败
    	at java.text.DateFormat.parse(DateFormat.java:364)
    	at io.binghe.concurrent.lab06.SimpleDateFormatTest01.lambda$main$0(SimpleDateFormatTest01.java:47)
    线程:pool-1-thread-13 格式化日期失败	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    
    	at java.lang.Thread.run(Thread.java:748)
    java.lang.NumberFormatException: For input string: ""
    	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    线程:pool-1-thread-20 格式化日期失败	at java.lang.Long.parseLong(Long.java:601)
    	at java.lang.Long.parseLong(Long.java:631)
    
    	at java.text.DigitList.getLong(DigitList.java:195)
    	at java.text.DecimalFormat.parse(DecimalFormat.java:2084)
    	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
    	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    	at java.text.DateFormat.parse(DateFormat.java:364)
    	at io.binghe.concurrent.lab06.SimpleDateFormatTest01.lambda$main$0(SimpleDateFormatTest01.java:47)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    java.lang.NumberFormatException: For input string: ""
    	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    	at java.lang.Long.parseLong(Long.java:601)
    	at java.lang.Long.parseLong(Long.java:631)
    	at java.text.DigitList.getLong(DigitList.java:195)
    	at java.text.DecimalFormat.parse(DecimalFormat.java:2084)
    	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
    	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    	at java.text.DateFormat.parse(DateFormat.java:364)
    
    Process finished with exit code 1
    

    说明,在高并发下使用SimpleDateFormat类格式化日期时抛出了异常,SimpleDateFormat类不是线程安全的!!!

    接下来,我们就看下,SimpleDateFormat类为何不是线程安全的。

    SimpleDateFormat类为何不是线程安全的?

    那么,接下来,我们就一起来看看真正引起SimpleDateFormat类线程不安全的根本原因。

    通过查看SimpleDateFormat类的源码,我们得知:SimpleDateFormat是继承自DateFormat类,DateFormat类中维护了一个全局的Calendar变量,如下所示。

    /**
      * The {@link Calendar} instance used for calculating the date-time fields
      * and the instant of time. This field is used for both formatting and
      * parsing.
      *
      * <p>Subclasses should initialize this field to a {@link Calendar}
      * appropriate for the {@link Locale} associated with this
      * <code>DateFormat</code>.
      * @serial
      */
    protected Calendar calendar;
    

    从注释可以看出,这个Calendar对象既用于格式化也用于解析日期时间。接下来,我们再查看parse()方法接近最后的部分。

    @Override
    public Date parse(String text, ParsePosition pos){
        ################此处省略N行代码##################
        Date parsedDate;
        try {
            parsedDate = calb.establish(calendar).getTime();
            // If the year value is ambiguous,
            // then the two-digit year == the default start year
            if (ambiguousYear[0]) {
                if (parsedDate.before(defaultCenturyStart)) {
                    parsedDate = calb.addYear(100).establish(calendar).getTime();
                }
            }
        }
        // An IllegalArgumentException will be thrown by Calendar.getTime()
        // if any fields are out of range, e.g., MONTH == 17.
        catch (IllegalArgumentException e) {
            pos.errorIndex = start;
            pos.index = oldStart;
            return null;
        }
        return parsedDate;
    }
    

    可见,最后的返回值是通过调用CalendarBuilder.establish()方法获得的,而这个方法的参数正好就是前面的Calendar对象。

    接下来,我们再来看看CalendarBuilder.establish()方法,如下所示。

    Calendar establish(Calendar cal) {
        boolean weekDate = isSet(WEEK_YEAR)
            && field[WEEK_YEAR] > field[YEAR];
        if (weekDate && !cal.isWeekDateSupported()) {
            // Use YEAR instead
            if (!isSet(YEAR)) {
                set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
            }
            weekDate = false;
        }
    
        cal.clear();
        // Set the fields from the min stamp to the max stamp so that
        // the field resolution works in the Calendar.
        for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
            for (int index = 0; index <= maxFieldIndex; index++) {
                if (field[index] == stamp) {
                    cal.set(index, field[MAX_FIELD + index]);
                    break;
                }
            }
        }
    
        if (weekDate) {
            int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1;
            int dayOfWeek = isSet(DAY_OF_WEEK) ?
                field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
            if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) {
                if (dayOfWeek >= 8) {
                    dayOfWeek--;
                    weekOfYear += dayOfWeek / 7;
                    dayOfWeek = (dayOfWeek % 7) + 1;
                } else {
                    while (dayOfWeek <= 0) {
                        dayOfWeek += 7;
                        weekOfYear--;
                    }
                }
                dayOfWeek = toCalendarDayOfWeek(dayOfWeek);
            }
            cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek);
        }
        return cal;
    }
    

    在CalendarBuilder.establish()方法中先后调用了cal.clear()与cal.set(),也就是先清除cal对象中设置的值,再重新设置新的值。由于Calendar内部并没有线程安全机制,并且这两个操作也都不是原子性的,所以当多个线程同时操作一个SimpleDateFormat时就会引起cal的值混乱。类似地, format()方法也存在同样的问题。

    因此, SimpleDateFormat类不是线程安全的根本原因是:DateFormat类中的Calendar对象被多线程共享,而Calendar对象本身不支持线程安全。

    那么,得知了SimpleDateFormat类不是线程安全的,以及造成SimpleDateFormat类不是线程安全的原因,那么如何解决这个问题呢?接下来,我们就一起探讨下如何解决SimpleDateFormat类在高并发场景下的线程安全问题。

    解决SimpleDateFormat类的线程安全问题

    解决SimpleDateFormat类在高并发场景下的线程安全问题可以有多种方式,这里,就列举几个常用的方式供参考,大家也可以在评论区给出更多的解决方案。

    1.局部变量法

    最简单的一种方式就是将SimpleDateFormat类对象定义成局部变量,如下所示的代码,将SimpleDateFormat类对象定义在parse(String)方法的上面,即可解决问题。

    package io.binghe.concurrent.lab06;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 局部变量法解决SimpleDateFormat类的线程安全问题
     */
    public class SimpleDateFormatTest02 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                            simpleDateFormat.parse("2020-01-01");
                        } catch (ParseException e) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }catch (NumberFormatException e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    此时运行修改后的程序,输出结果如下所示。

    所有线程格式化日期成功
    

    至于在高并发场景下使用局部变量为何能解决线程的安全问题,会在【JVM专题】的JVM内存模式相关内容中深入剖析,这里不做过多的介绍了。

    当然,这种方式在高并发下会创建大量的SimpleDateFormat类对象,影响程序的性能,所以,这种方式在实际生产环境不太被推荐。

    2.synchronized锁方式

    将SimpleDateFormat类对象定义成全局静态变量,此时所有线程共享SimpleDateFormat类对象,此时在调用格式化时间的方法时,对SimpleDateFormat对象进行同步即可,代码如下所示。

    package io.binghe.concurrent.lab06;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 通过Synchronized锁解决SimpleDateFormat类的线程安全问题
     */
    public class SimpleDateFormatTest03 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
        //SimpleDateFormat对象
        private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            synchronized (simpleDateFormat){
                                simpleDateFormat.parse("2020-01-01");
                            }
                        } catch (ParseException e) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }catch (NumberFormatException e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    此时,解决问题的关键代码如下所示。

    synchronized (simpleDateFormat){
    	simpleDateFormat.parse("2020-01-01");
    }
    

    运行程序,输出结果如下所示。

    所有线程格式化日期成功
    

    需要注意的是,虽然这种方式能够解决SimpleDateFormat类的线程安全问题,但是由于在程序的执行过程中,为SimpleDateFormat类对象加上了synchronized锁,导致同一时刻只能有一个线程执行parse(String)方法。此时,会影响程序的执行性能,在要求高并发的生产环境下,此种方式也是不太推荐使用的。

    3.Lock锁方式

    Lock锁方式与synchronized锁方式实现原理相同,都是在高并发下通过JVM的锁机制来保证程序的线程安全。通过Lock锁方式解决问题的代码如下所示。

    package io.binghe.concurrent.lab06;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 通过Lock锁解决SimpleDateFormat类的线程安全问题
     */
    public class SimpleDateFormatTest04 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
        //SimpleDateFormat对象
        private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //Lock对象
        private static Lock lock = new ReentrantLock();
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            lock.lock();
                            simpleDateFormat.parse("2020-01-01");
                        } catch (ParseException e) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }catch (NumberFormatException e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }finally {
                            lock.unlock();
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    通过代码可以得知,首先,定义了一个Lock类型的全局静态变量作为加锁和释放锁的句柄。然后在simpleDateFormat.parse(String)代码之前通过lock.lock()加锁。这里需要注意的一点是:为防止程序抛出异常而导致锁不能被释放,一定要将释放锁的操作放到finally代码块中,如下所示。

    finally {
    	lock.unlock();
    }
    

    运行程序,输出结果如下所示。

    所有线程格式化日期成功
    

    此种方式同样会影响高并发场景下的性能,不太建议在高并发的生产环境使用。

    4.ThreadLocal方式

    使用ThreadLocal存储每个线程拥有的SimpleDateFormat对象的副本,能够有效的避免多线程造成的线程安全问题,使用ThreadLocal解决线程安全问题的代码如下所示。

    package io.binghe.concurrent.lab06;
    
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 通过ThreadLocal解决SimpleDateFormat类的线程安全问题
     */
    public class SimpleDateFormatTest05 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
    
        private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(){
            @Override
            protected DateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd");
            }
        };
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            threadLocal.get().parse("2020-01-01");
                        } catch (ParseException e) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }catch (NumberFormatException e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    通过代码可以得知,将每个线程使用的SimpleDateFormat副本保存在ThreadLocal中,各个线程在使用时互不干扰,从而解决了线程安全问题。

    运行程序,输出结果如下所示。

    所有线程格式化日期成功
    

    此种方式运行效率比较高,推荐在高并发业务场景的生产环境使用。

    另外,使用ThreadLocal也可以写成如下形式的代码,效果是一样的。

    package io.binghe.concurrent.lab06;
    
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 通过ThreadLocal解决SimpleDateFormat类的线程安全问题
     */
    public class SimpleDateFormatTest06 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
    
        private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
    
        private static DateFormat getDateFormat(){
            DateFormat dateFormat = threadLocal.get();
            if(dateFormat == null){
                dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                threadLocal.set(dateFormat);
            }
            return dateFormat;
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            getDateFormat().parse("2020-01-01");
                        } catch (ParseException e) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }catch (NumberFormatException e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    5.DateTimeFormatter方式

    DateTimeFormatter是Java8提供的新的日期时间API中的类,DateTimeFormatter类是线程安全的,可以在高并发场景下直接使用DateTimeFormatter类来处理日期的格式化操作。代码如下所示。

    package io.binghe.concurrent.lab06;
    
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 通过DateTimeFormatter类解决线程安全问题
     */
    public class SimpleDateFormatTest07 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
    
       private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            LocalDate.parse("2020-01-01", formatter);
                        }catch (Exception e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    可以看到,DateTimeFormatter类是线程安全的,可以在高并发场景下直接使用DateTimeFormatter类来处理日期的格式化操作。

    运行程序,输出结果如下所示。

    所有线程格式化日期成功
    

    使用DateTimeFormatter类来处理日期的格式化操作运行效率比较高,推荐在高并发业务场景的生产环境使用

    6.joda-time方式

    joda-time是第三方处理日期时间格式化的类库,是线程安全的。如果使用joda-time来处理日期和时间的格式化,则需要引入第三方类库。这里,以Maven为例,如下所示引入joda-time库。

    <dependency>
    	<groupId>joda-time</groupId>
    	<artifactId>joda-time</artifactId>
    	<version>2.9.9</version>
    </dependency>
    

    引入joda-time库后,实现的程序代码如下所示。

    package io.binghe.concurrent.lab06;
    
    import org.joda.time.DateTime;
    import org.joda.time.format.DateTimeFormat;
    import org.joda.time.format.DateTimeFormatter;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 通过DateTimeFormatter类解决线程安全问题
     */
    public class SimpleDateFormatTest08 {
        //执行总次数
        private static final int EXECUTE_COUNT = 1000;
        //同时运行的线程数量
        private static final int THREAD_COUNT = 20;
    
        private static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd");
    
        public static void main(String[] args) throws InterruptedException {
            final Semaphore semaphore = new Semaphore(THREAD_COUNT);
            final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < EXECUTE_COUNT; i++){
                executorService.execute(() -> {
                    try {
                        semaphore.acquire();
                        try {
                            DateTime.parse("2020-01-01", dateTimeFormatter).toDate();
                        }catch (Exception e){
                            System.out.println("线程:" + Thread.currentThread().getName() + " 格式化日期失败");
                            e.printStackTrace();
                            System.exit(1);
                        }
                        semaphore.release();
                    } catch (InterruptedException e) {
                        System.out.println("信号量发生错误");
                        e.printStackTrace();
                        System.exit(1);
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            executorService.shutdown();
            System.out.println("所有线程格式化日期成功");
        }
    }
    

    这里,需要注意的是:DateTime类是org.joda.time包下的类,DateTimeFormat类和DateTimeFormatter类都是org.joda.time.format包下的类,如下所示。

    import org.joda.time.DateTime;
    import org.joda.time.format.DateTimeFormat;
    import org.joda.time.format.DateTimeFormatter;
    

    运行程序,输出结果如下所示。

    所有线程格式化日期成功
    

    使用joda-time库来处理日期的格式化操作运行效率比较高,推荐在高并发业务场景的生产环境使用。

    解决SimpleDateFormat类的线程安全问题的方案总结

    综上所示:在解决解决SimpleDateFormat类的线程安全问题的几种方案中,局部变量法由于线程每次执行格式化时间时,都会创建SimpleDateFormat类的对象,这会导致创建大量的SimpleDateFormat对象,浪费运行空间和消耗服务器的性能,因为JVM创建和销毁对象是要耗费性能的。所以,不推荐在高并发要求的生产环境使用

    synchronized锁方式和Lock锁方式在处理问题的本质上是一致的,通过加锁的方式,使同一时刻只能有一个线程执行格式化日期和时间的操作。这种方式虽然减少了SimpleDateFormat对象的创建,但是由于同步锁的存在,导致性能下降,所以,不推荐在高并发要求的生产环境使用。

    ThreadLocal通过保存各个线程的SimpleDateFormat类对象的副本,使每个线程在运行时,各自使用自身绑定的SimpleDateFormat对象,互不干扰,执行性能比较高,推荐在高并发的生产环境使用。

    DateTimeFormatter是Java 8中提供的处理日期和时间的类,DateTimeFormatter类本身就是线程安全的,经压测,DateTimeFormatter类处理日期和时间的性能效果还不错(后文单独写一篇关于高并发下性能压测的文章)。所以,推荐在高并发场景下的生产环境使用。

    joda-time是第三方处理日期和时间的类库,线程安全,性能经过高并发的考验,推荐在高并发场景下的生产环境使用

    写在最后

    如果你想进大厂,想升职加薪,或者对自己现有的工作比较迷茫,都可以私信我交流,希望我的一些经历能够帮助到大家~~

    推荐阅读:

    好了,今天就到这儿吧,小伙伴们点赞、收藏、评论,一键三连走起呀,我是冰河,我们下期见~~

    展开全文
  • 六大环境风险分类

    千次阅读 2020-12-30 08:18:04
    六大环境风险分类一、生产重大安全事故次生引发灾害环境事件风险主要指因火灾爆炸等生产安全事故可能引发的环境事件风险。二、危险化学晶泄漏造成引发人员伤害和或环境损害事件风险伤害主要指危险化学晶生产、储存、...

    六大环境风险分类

    一、

    生产重大安全事故次生引发灾害环境事件风险

    主要指因火灾爆炸等生产安全事故可能引发的环境事件风险。

    二、

    危险化学晶泄漏造成引发人员伤害和或环境损害事件风险伤害

    主要指危险化学晶生产、储存、使用过程中(油气田企业、管

    道企业除夕川,以及公路、铁路、内河运输过程中,因危险化

    学晶泄漏累积突发或突发泄漏可能造成的环境事件风险。

    三、

    油气泄漏引发污染环境事件风险

    主要指集输、长输管道运行以及主要指钻井、井下作业、采油

    气过程中的油气泄漏和油气管道泄漏可能造成的环境事件风

    险。

    四、

    放射源丢失、被盗、失控散失和落井引发治安事件风险

    主要指放射源丢失、被盗和失控可能引发的环境事件风险。

    五、

    环境问题违法引发重大社会影响造成群体性事件风险

    主要指卫生防护距寓范围内存在大气环境保护目标、建设项目

    违反相关环境保护管理程序、未经批准在环境敏感区违法违规

    建有建设生产设施、噪声和恶臭扰民等可能引发的环境事件风

    险。

    六、

    三废”污染物超标违法排放造成引发污染环境事件风险

    主要指污染物未按要求处理处置、污染治理设施不能满足排放

    标准或总量控制要求、监测数据弄虚作假等可能造成的环境事

    件风险。

    展开全文
  • 数据安全分类分级剖析

    千次阅读 2021-09-15 00:04:46
    数据分类分级对于数据的安全管理至关重要,安全分类分级是一个“硬核课题”,从数据治理开始,除了标准化和价值应用,重要的课题就是质量+安全安全是底线,是价值应用的前提和基础。数据分类可以为数据资产结构化...

    概述

    **本人博客网站 **IT小神 www.itxiaoshen.com

    数据分类分级管理不仅是加强数据交换共享、提升数据资源价值的前提条件,也是数据安全保护场景下的必要条件。《数据安全法》规定国家建立数据分类分级保护制度,对数据实行分类分级保护,数据分类分级工作是基础和核心,数据分类分级是数据使用管理和安全防护的基础,为数据尤其是重要数据制定分类分级制度并依规管理,是实现数据安全目标的重要工作;数据分类分级不仅是数据安全治理的第一步,也是当前数据安全治理的痛点和难点,数据分类分级是非常有挑战性的工作。总结数据分类分级的基本流程包括4个重要的方面

    image-20210913172423347

    该流程具有科学的方法论指导作用,但其问题在于缺乏实践过程中的具体方法。数据分类分级的行业差异性很大,不同行业数据具有不同的属性和业务处理目标,在开展数据分类分级时,需要深入理解行业业务需求,研究设计具有针对性的方法和工具。

    数据分类分级对于数据的安全管理至关重要,安全分类分级是一个“硬核课题”,从数据治理开始,除了标准化和价值应用,重要的课题就是质量+安全。安全是底线,是价值应用的前提和基础。数据分类可以为数据资产结构化管理、UEBA(用户及实体行为分析)、个人信息画像等数据治理工作提供有效支撑;数据分级通过对不同级别的数据设置相应访问权限、加密规则、脱敏规则等,可大大提升数据安全管控效率,是数据安全精细化管理的重要一步

    政务数据分类分级的痛点问题和挑战

    数据分类面临的痛点问题和挑战

    • 如何选择分类维度的问题

    对于数据进行分类可以有很多维度,包括基于数据形式数据内容等。基于数据形式可以按照数据的存储方式、数据更新频率、数据所处地理位置、数据量等进行分类;数据内容可以根据数据所涉及的主体、业务维度等多个维度进行分类。

    不同维度各有价值,如何选择一个维度对数据进行分类需要考虑数据分类的目的,但很多时候大家都希望通过一个分类维度实现多个目标,或者将两个分类维度混合进行分类。分类维度的不清晰会导致后续基于分类的很多操作都存在问题

    • 单一分类维度下的类别划分问题

    例如,基于内容进行分类的维度,面临数据可能分类不全、类别不清晰的问题。主要原因是大范围内的内容分类是一个很复杂的问题,甚至可能涉及知识分类的问题,这在目前还是一个较为难以解决的问题。类别划分有问题会导致有些数据无法分到一个分类下,而有些数据又同属于两个分类

    数据分级面临的痛点问题和挑战

    • 定性到定量的问题

    针对信息资源的分级,需要根据信息内容确定。目前尚无科学的方法和范式支撑构建信息内容的数学模型,因此很难准确定量地进行数据内容描述。

    举个例子,我国目前已有一些针对政务信息资源的安全级别描述,其中有按损害影响程度进行的数据定级,但没有关于影响程度定量的描述,所谓针对公民的损害,是造成财产损失还是身体伤害?造成什么量级的财产损失?这样的描述难以在实际操作过程中给定级的人员准确的依据去判断政务信息资源属于哪一个级别

    • 分级的级数问题

    在政府部门进行政务信息资源分级时,需要找到一个合适的级数,使得在使用过程中达到效率和安全管控的平衡。过多的分级会给实际使用带来困难,太少的分级又会使得管控难以准确地约束数据。

    目前针对不涉密的政务信息资源主要分为非密内部两级,但是在实际使用过程中这两个级别并不能满足对于数据处理的需求,并不是所有非密的数据都适合让公众知晓,也不是所有内部数据都只能政府部门使用,因此将不涉密的的政务信息资源只简单的分为两级是不合适的。

    • 分级的粒度问题

    在进行分级的时候,分级的粒度是影响分级效果的主要因素之一。以什么样的粒度进行分级才可以既达到分级防护的目的,同时不影响正常的业务仍是一个有待进一步研究明确的问题。

    政府部门的信息资源涉及各行各业,数据存储的格式众多,有文件、表、行列、字段等不同的数据粒度。不同行业中影响信息资源级别的属性要素也不一,例如地理信息资源地图的比例尺和所包含的地图元素是影响信息资源的级别的关键因素。

    • 分级的有效落实问题

    有些地方政府专门成立了大数据管理部门,来规范政府部门对信息资源的共享使用,也出台了相关的数据共享条例、数据安全保障条例等,但是还缺乏完整的流程和环节来完成从数据梳理、数据分类分级到数据存储保护、数据共享使用。

    现有的数据使用模式,是以部门为单位,各自负责自己所拥有的数据,因此相应的规章制度更多注重部门内部,缺乏跨部门的数据使用规范。目前相对成熟的跨部门的具体数据规范主要是公安部门的人口库信息,但是其他部门相对较弱。

    • 数据的升降级方法问题

    政务信息资源是动态变化的,因此数据会发生合并、摘抄等简单操作,也会进行分析融合等复杂操作。这些操作会对已经进行了分级的政务信息资源的级别产生变化。而由于政务信息资源众多,不同部门对信息资源的使用方式、需求粒度都不统一,信息资源的级别发生变化时,人工重新判定的标准难以统一,也无法完全以自动化的方式进行。

    数据安全分类分级

    基础理念

    • 数据资产和元数据关联,是数据安全最终的落脚点;
    • 数据定级,这个是数据安全定级的操作标准,从数据标准引申到定级标准,然后为后续的技术性措施提供指引;
    • 安全策略,这个是数据分类分级的真正核心,就是当有了一套所谓的管理制度和规范后,具体如何衔接到纯粹的技术措施和方法,从制度到方法,中间需要一个“实施策略”。
      • 这里的安全策略,是一个基于数据环境,同时主要从数据环境的变更作为“管控点”的策略。它的基本思路是:
      • 数据是依托于环境进行采集、存储的,在企业的实际工作中,就静止数据而言,环境的安全策略已经基本覆盖了数据的安全策略,包括系统、网络、用户权限等。
      • 只有在环境发生变更,就是数据出现了传输等过程,从一个环境变迁到另一个环境,这个时候,静止数据的环境安全策略无法覆盖,需要就环境变更产生的动态情况进行安全策略的制定,这就是数据的脱敏、加密等技术保护措施的实施动因

    在现代企业中,静止数据的安全措施总体上是有一定基础的,相对于动态数据而言也是更加丰富和完整的。比如物理的机房准入,网络的访问控制,防火墙的管理,用户访问权限,数据生命周期的管理等等。薄弱点在于动态数据部分。比如,当一份生产数据要传输到第三方,这个时候如何处理?谁负责这个事情?具体要做什么处理?谁实施这个操作?在什么地方进行?这些内容,就容易出现空白。

    所以,数据安全分类分级工作,要从企业实际情况出发,不是枉顾实际情况,单纯援引理论直接单搞一套重复建设,而是要和企业已有的安全基础设施、制度体系框架、组织结构和流程机制等结合,从痛点入手,查漏补缺,快速的补短板,形成一套更加完整的数据安全管控体系。

    而这套体系如果仅仅停留在《办法》、《规范》、《指引》上,那还是不接地气,最终要平台化、系统化。通过数据资产盘点、数据标准制定、数据安全定级的索引,再通过数据溯源定位好数据主人,基本上可以在系统平台上解决“WHO”的问题和动员组织能力提供了一个抓手。

    把做什么想明白,把谁来做想明白,把怎么做平台化

    数据安全分类分级理解

    数据分类

    数据分类是指企业、组织的数据按照部门归属、业务属性、行业经验等维度对数据进行类别划分,是个系统的复杂工程。数据分类的目的是要便于数据的管理、利用。基本原则是:分类要合理,即在一个明确的业务目标下,确定逻辑清晰的分类维度,并确保数据有且只有一个分类类别。可以从三个维度进行分类

    • 数据管理维度:根据数据的一些客观属性进行分类,便于数据管理机构对数据进行管理,便于数据管理系统的规划
    • 数据应用维度:根据数据内容的固有属性进行分类,便于数据理解和应用
    • 数据所涉及的对象维度:对数据内容的理解的维度,不过更偏向于支撑便于数据权属分析和数据安全管理

    数据分级

    数据分级则是从数据安全、隐私保护和合规的角度对数据的敏感程度进行等级划分。整体来看,建议在数据分类的基础上,根据某类数据的安全属性(如完整性、保密性、可用性),集合数据在经济社会发展中的重要程度,以及一旦遭到篡改、破坏、泄露或者非法获取、非法利用时,对国家安全、公共利益或者公民、组织合法权益造成的危害程度,结合自身组织情况将数据分为4—5个安全保护级别

    • 针对定性到定量的问题,需要按照行业需求,结合科学的方法,进行数据信息模型的研究。在实际工作中,可先行结合业务经验进行总结和实践尝试;
    • 针对数据分级级数如何确定的问题,根据Gartner报告表明,合理的数据分级最好在3-5级之间,太多会造成大量的管理负担,不利于正常的实施。在政务信息共享领域,可参考2017年发改委发布的《政务信息资源目录编制指南》文件中的数据分级的描述(见下表),并结合本部门业务实际情况进行研究,确定适合的分级级数;
    • 针对分级粒度的问题,并无标准化的粒度划分方法,实际工作中又可从3个方面进行评估确定:
      • 首先,需要考虑数据会用来干什么,例如查询统计、建模分析、数据密布型人工智能算法。进行查询统计的数据可以针对查询项和统计项进行细粒度的定级,其他项可以适当增大分级粒度;
      • 其次,要考虑数据的处理方式,例如原始数据未改变、融合产生新数据、剪裁产生新数据、更新等。若原始数据未改变,信息资源分级的粒度可以适量大一些;若要融合产生新数据,分级粒度应当更细一些,避免数据融合分析过程中,暴露原本想隐藏的信息,导致原级别定义不准确;
      • 第三,参考数据在信息系统中的存储和处理方式进行定级粒度划分。结构化和半结构化的信息资源在定级的时候,可以根据用途按照行列或者表级的粒度来定级。非结构化的信息资源定级的粒度建议以单个文件的粒度进行;
    • 针对数据分级如何落实的问题,以政务信息共享为例,需要建立更为完善的数据分级流程,理清数据分级在政务信息共享工作中的位置。同时建立分级人员的培训制度、分级的责任制度等,使得对政务信息资源分级能够切实的实施;
    • 针对数据的升降级方法问题,需要制定一系列数据分级的升降级原则,明确在什么情况下数据会发生生升降级变化,通过判断哪些要素进行升降级处理,并制定有效的自动化升降级信息资源预处理机制。

    数据定级流程

    数据安全定级过程包括数据资产梳理、数据安全定级准备、数据安全级别判定、数据安全级别审核及数据安全级别批准

    image-20210914151039731

    数据定级流程基本步骤

    • 数据资产梳理:
      • 第一步:对数据进行盘点、梳理与分类,形成统一的数据资产清单,并进行数据安全定级合规性相关准备工作。
    • 数据安全分级准备:
      • 第二步:明确数据分级的颗粒度( 如库文件、表、字段等) ;
      • 第三步:识别数据安全定级关键要素(影响对象、影响范围、影响程度)。
    • 数据安全级别判定:
      • 第四步:按照数据定级规则,结合国家及行业有关法律法规、部门规章,对数据安全等级进行初步判定;
      • 第五步:综合考虑数据规模、数据聚合、数据时效性、数据形态(如是否经汇总、加工、统计、脱敏或匿名化处理等)等因素,对数据安全级别进行复核,调整形成数据安全级别评定结果及定级清单。
    • 数据安全级别审核:
      • 第六步:审核数据安全级别评定过程和结果,必要时重复第三步及其后工作,直至安全级别的划定与本单位数据安全保护目标相一致。
    • 数据安全级别批准:
      • 第七步:最终由数据定级工作领导组织对数据安全分级结果进行审议批准。

    数据级别变更

    数据级别变更应由数据的主管业务部门/属主部门或数据安全管理部门发起,并按照数据定级流程实施。在数据定级完成后出现下列情形时,应对相关数据的安全级别进行变更:

    • 数据内容发生变化,导致原有数据的安全级别不适用变化后的数据;
    • 数据内容未发生变化,但因数据时效性、数据规模、数据应用场景、数据加工处理方式等发生变化,导致原定的数据级别不再适用;
    • 不同数据类型经汇聚融合形成新的数据类别,使得原有的数据级别不适用,应重新进行级别判定;
    • 因国家或行业主管部门要求,导致原定的数据级别不再适用;
    • 需要对数据级别进行变更的其它情形。

    安全管控策略

    根据数据分类分级结果,从管理、流程和技术等方面,制定基于数据安全视角的全生命周期数据安全管控策略,管理方面包括不限于规范管理决策职责、规范日常维护职责、规范岗位人员职责等;流程方面包括不限于制定数据安全管理整体机制流程安全管控策略、权限管理操作流程管控策略等;技术方面包括不限于制定基础架构的整体安全支撑技术、加密、脱敏、数据防泄漏等的管控策略。

    国内企业数据分类分级产品

    卫士通

    卫士通牵头、参与了数据安全领域的多个国家及地方的标准研究和编制,包括《信息安全技术 大数据安全管理指南》、《信息安全技术 大数据服务安全能力要求》、《政务信息资源安全分级指南》、《雄安新区数据资源目录定级指南》、《雄安集团数据使用暂行办法》等。

    在政务、金融、交通、智慧城市和大型央企等项目中,建设探索解决政务信息共享环节数据缺乏分类分级防护、数据权责难以界定以及数据流转监管困难等问题,针对政务数据资源管理缺乏分类分级方法、数据权责难以界定、数据流转监管困难、企业数据权限管理困难、金融数据分级标准落地等问题,卫士通总结形成了涵盖数据分类分级工作的数据安全解决方案,并在项目中进行了不同程度的落地实践。我们希望基于当前的研究和工作,能够帮助用户确定本单位的数据安全分类分级管理制度、标准,协助用户完成已有数据定权分级,提供基于数据分类分级后的安全防护方案设计和建设服务。

    在实际项目中,卫士通也已积累沉淀形成了专业的数据分类分级产品,如下图所示

    image-20210914110634588

    该产品通过自动化技术,将分类分级的专家经验和方法固化为规则模型和识别引擎,有效避免了采用全人工进行数据分类分级时存在的因人员经验背景知识不足导致的不确定性问题,并且降低了人力成本。

    同时,在具体实施过程中根据不同场景,可与数据资产管理系统、传统数据库、大数据库等进行对接,还可根据不同行业选择不同的识别引擎,通过识别关键要素,结合分类分级的规则进行自动化分类分级

    深信服

    深信服智能数据分类分级平台引入了人工智能与机器学习算法,相较于传统数据分类分级做法,采用机器学习技术,大大提升了准确率,进一步提升了工作效率,减少了人力成本,在数据分类分级上作了一次有效实践,深信服智能数据分类分级平台工作机制如下:

    • 分类分级策略定义
      • 平台内置通用的分类分级策略,用户可根据国家与行业相关的数据分类分级标准和规范进行设置,其中数据分类策略用于定义数据的类型,数据分级策略用于定义数据的安全等级。
    • 多维数据特征提取
      • 平台能够对接各种类型的数据库,实现数据资产的自动发现和数据目录的生成,通过机器学习算法对数据进行多维度元数据特征向量自动提取,对相似字段的数据字段进行聚合归类。
    • 智能分类分级推荐
      • 平台接着会对相似数据类别与级别进行智能推荐,实现数据的智能分类分级,同时在用户分类分级过程中也会不断学习用户对数据的标注,提升智能推荐率,目前分类分级智能推荐率达到90%以上。

    同时,分类分级结果以API的形式对外开放,业务系统和安全系统均可以调用API,以根据数据的分类分级结果进行精细化的数据管控与安全防护。

    深信服以智能数据分类分级为核心的数据共享安全解决方案荣获贵州数博会 2021 年“数字政府方案案例创新奖”,在行业内已小有名气

    安恒信息

    自动化数据分类分级打标

    标签化可以通过对数据打标签的方式降低数据安全管理的门槛,帮助单位进行数据的分类管理,分级防护。目前业内的专用工具可基于关联补齐后的数据,结合数据分类分级结果,在原数据基础上进行标记。

    • 结构化数据的打标过程
      • 工具自动方式
        • 工具自动打标签可以通过两种方式实现,一种是通过从数据库中提取元数据,进行自动分级分类,分级分类策略可配置。另一种为借助敏感标签能力,对元数据中的敏感程度和数据定级自动智能推荐,并快速完成数据分级管理。同时自动化工具能够支持数据分级支持对表、字段进行识别和分级标识,可自定义定级规则,并支持标记和变更数据敏感级别,通用的敏感级别包括公开、内部、敏感、机密等。
      • 机器学习方式
        • 目前业内智能化打标一般指的是针对敏感数据进行打标。借助正则表达式、关键词、文档指纹、OCR、机器学习、自然语言处理等先进AI技术提取敏感数据特征,建立相应敏感识别规则,然后统一录入规则引擎。识别规则除机器学习获得以外,还包括系统内置规则及用户根据敏感特征自定义规则,可进行精确的、更多场景的敏感数据识别。识别后的数据与敏感标签库进行匹配,命中规则数据则会打上相应标签,根据标签则可以查看数据分级分类结果以及敏感数据分布情况。
    • 非结构化数据的打标过程
      • 针对文档、图像、视频等非结构化数据,通过标记文件头的方式进行打标。

    image-20210914153105797

    基于数据分类分级的某市政务数据安全管控实践

    政务数据由基础信息、行业、主题等各类别的结构化、非结构化数据的汇集而成。某市政数据为规范市政数局、区委办局两级数据管理的相关标准,规范政务数据安全管控的规则,基于政务数据分类分级管理方法论进行了数据安全管控。

    工作流程

    image-20210914154226904

    制度建设

    由政务数据主管部门牵头,信息安全部门制定分类分级相关的制度规范,包括组织人员岗位职责规范、分类分级规范、分类分级矩阵(含定级方法、安全管控策略)等。

    培训推广

    由政务数据主管部门组织,信息安全部门为业务部门提供数据安全培训,除了针对分类分级制度规范解读、工具使用、安全管控实施细则等,培训内容还涵盖数据安全的常识、数据加密方式方法、数据脱敏方式方法、数据防泄漏等相关方面。通过开展不同角色的安全培训,覆盖政务管理培训和技术培训,将数据安全理论、数据安全最佳实践赋能XX市政务人员,达到培训提高数据安全意识、增强数据分类分级能力的目的。

    实施落地

    • 梳理数据现状。业务部门梳理本部门的全量数据范围,明确数据产生方式、数据结构化特征、数据更新频率、数据应用情况、数据质量情况、数据敏感程度等。
    • 初步确定数据分类分级。依据GB/T 21063.6-2007政务信息资源目录体系第4部分:政务信息资源分类相关要求,业务部门结合自身业务,初步判定数据在确定各分类维度的分类类别和数据安全等级。
    • 部门自主审核。业务部门应对数据在各维度的初步分类结果及数据分级结果进行部门内部自主审核,审核通过后提交至政务数据主管部门审查。
    • 数据分类示例:

    image.png

    • 数据分类分级管控策略矩阵示例

    image.png

    检查评审

    合规性审查。政务数据主管部门对本级及下级业务部门的数据分类和分级结果进行合规性审查。经政务数据主管部门合规性审查通过后,最终确定业务部门的数据在各维度分类下的结果和数据安全等级。

    安全管控

    • 确定最终数据分类分级。经政务数据主管部门合规性审查通过后,最终确定业务部门的数据分类分级结果。
    • 数据安全分级管控。依据数据分级分类规范中的分级管控要求,落实具体管控措施。
    • 变更维护。业务部门应定期组织对分类分级结果的合理性、有效性进行评估,当数据状态、服务范围等方面发生变化时,及时对分类分级结果进行调整,并记录变更过程。

    安华金和

    帮助组织梳理数据资产,制定数据分类分级的标准指南,制定切实可落地的数据安全策略,从而保障数据安全治理工作的顺利开展。

    image-20210914161240136

    国外企业数据分类分级产品

    Netwrix数据分类软件

    Netwrix是美国一家提供信息安全与治理技术的网络安全公司,为用户提供以数据为中心的安全服务,被评为2020年Gartner文件分析软件市场指南“代表性供应商”、2020年Gartner Peer Insight文件分析软件“客户之选”

    Netwrix数据分类平台概述

    Netwrix数据分类平台通过使用数据发现和分类工具(Data Discovery and Classification Tool,简称DDC)实现分类功能。工具自动识别不同应用程序的结构化和非结构化数据,并结合预定义的分类法对文件进行分类,基于分类结果展示数据的分布状态统计。

    无需部署客户端,使用基于WEB的管理控制台执行数据分类操作;通过HTTP协议和第三方应用API接口定位数据源;支持预定义的分类规则,实现对受GDPR、GLBA、HIPAA和其他监管标准保护数据的识别,也可以通过自定义分类规则查询识别其他数据;使用逻辑化和持续化的全文本索引模式,配合使用机器学习算法、语义分析自动查询文件内容;并为每种分类规则设置关联度得分,得分值可依据数据分类结果实时调整,用以调整文件匹配的范围;支持包括英语、德语、法语、汉语、日语、韩语等50余种语言的数据分类

    Netwrix数据分类平台功能

    Netwrix数据分类平台主要包括三个功能:数据采集、数据分类和数据分类结果的可视化呈现。上述功能通过基于WEB的管理控制台(Management Console)贯穿为一体,实现对分类过程的操作配置

    • 数据采集
      • 运行在数据分类服务器(Data Classification Server)上的数据分类采集服务(Data Classification Collector Service),采集数据源(Data Source)的文档后,将文档转换为纯文本,并形成文件元数据(Metadata)存储于数据分类SQL数据库(Data Classification SQL Database)。数据分类索引服务(Data Classification Index Service)基于收集的文档内容和元数据,创建全文本查询索引(Full-text Search Index),并将其存储至索引库(Data Classification Index)
      • 数据源是需采集和分类的数据存储库。通过管理控制台的数据源内容配置功能,实现对需采集数据源的添加和管理,添加后可查看数据采集结果
      • Netwrix支持分类的数据源有:Windows文件系统、Windows Server系列服务器、Linux文件系统(SMB/CIFS/NFS)、Office 365、数据库、Outlook(2010以上版本)、DropBox、Exchange服务器/邮箱、Google Drive、SharePoint等。在数据采集阶段,除了选择需采集的数据源类型,还需针对每种数据源配置相应的采集选项,以便于更精细化地定位
        • 数据库:Netwrix支持对SQL Server(2008以上版本)、Oracle、PostgreSQL、EMC等主流数据库内容的采集及分类。采集前需要先设置数据库访问用户名(如Windows服务或IIS程序池用户)或连接信息。数据库连接创建成功后,数据分类采集服务即可将采集到的内容智能映射为元数据。数据库内容采集的主要配置项如下:
          • 数据库类型。从SQLServer、Oracle、MySQL、PostgreSQL等选项中选取所需采集的数据库类型
          • 数据库服务器信息。设置采集目标数据库的服务器地址、具体数据库名称、登录用户名和身份认证方式
          • OCR处理模式。Netwrix可以通过OCR模式采集数据库文件中的图片内容,可从“禁用/默认路径/标准质量/增强质量”4种模式中选择
          • 数据库采集范围。设置需采集内容的数据库表、列的范围。
        • 文件系统:Netwrix支持对Windows文件系统和Linux文件系统的内容采集
          • 文件(夹)路径。设置需采集内容的文件(夹)路径
          • 文件夹级别。设置采集文件夹深度,可以选择是否包含子文件夹、是否采集所有子文件夹,以及子文件夹深度的范围(2-99级)
          • 文件夹访问信息。设置访问文件夹所需的系统帐户和密码,以及是否允许匿名访问文件目录
          • 重新索引周期。当源文件发生变更(增加/修改)后,Netwrix分类会定期更新索引,默认更新周期为7天
          • 文件类型。设置需采集的文件类型
          • 是否采集相同内容的副本文件,以及采集文件的优先级
        • 查看数据源采集结果
          • 数据采集流程自动对数据源进行采集、格式转换和创建索引的处理操作后,即可在管理控制台上查看数据源采集结果,包括:数据源类型、数据源文件位置、数据源采集状态、数据源索引创建状态、数据源采集文件数量及总大小
    • 数据分类
      • 数据分类服务(Data Classification Classifier Service)根据Netwrix预定义的第三方分类法(Taxonomies)和用户自定义的分类法,对文件内容匹配后分类,最终将分类结果存储于数据分类采集数据库(Data Classification Collector Database)中
      • Netwrix数据分类工具提供预定义分类法,这些分类法包括数百个现成的分类规则。每种分类法包含一系列术语(term),术语又由一系列配置规则(configuration clue)定义。通过使用规则与文件内容进行匹配,最终定位源文件的所属分类
        • 分类法
          • Netwrix数据分类平台所提供的预定义分类法共8种,其中4种核心分类法覆盖了个人、金融、医疗等领域,包括:财务信息(Financial Records)、PII(Personal Identifiable Information,个人可识别信息)、第三方支付行业数据安全标准(Payment Card Industry Data Security Standard,PCI DSS)、患者健康信息(Patient Health Information,PHI),余下4种衍生于核心分类法,用于满足部分特定的合规性要求,称为衍生分类法,包括:GDPR(通用数据保护条例)、GDPR第九章中涉及的个人信息特殊类别、GLBA(金融现代法案)、HIPAA(医疗保险可携性和责任法案)。除了上述预定义分类法外,用户也可以添加自定义分类法
        • 分类规则
          • 分类规则通过复合词精确/模糊匹配、区分大小写、单词发音、正则表达式、语种类型匹配等11种匹配方式,查询文件内容后对其分类。此外,用户也可以添加自定义分类规则,添加时可设置规则的分数,代表其与分类特征的关联度。分数越高,则关联度越高,此项规则可用于对文件进行分类的概率越大。
          • 分类规则用于描述文档中发现的语言,使得文档归属于特定的主题。Netwrix提供预定义分类规则用于查询文件内容,这些规则涵盖了如英语、法语、德语、西班牙语等多语种的个人可识别信息(姓名、家庭住址等),以及英国、新加坡、南非等多个国家的识别码和登记码
        • 分类标签
          • Netwrix支持将分类标签写入被采集数据的属性中。具体操作方式为:在管理控制台上,将分类标签写入到指定数据源的属性中。分类标签可采用[分类名称|分类ID]的格式呈现
          • 例如:农业分类法中有农场(ID为11)和生产(ID为32)两个子分类。当同时包含农业和生产的文件分类完成后,分类标签即写入该文件的属性中,即文件属性增加项——属性名称农业,属性值[农业|11;生产|32]
    • 分类结果展示
      • 通过查看管理控制台上的数据源及分类规则详细信息、统计审计报告如文件分布地图等功能,展示数据分类结果
      • 数据分类结束后,即可在管理控制台通过多种方式查看分类结果
        • 通过数据源查看
          • 选择某项数据源,即可查看已采集的数据信息,包括:文件名称、路径、分类状态、匹配的分类等内容。
          • 数据源查看文件分类结果
        • 通过规则查看
          • 选择分类法及其子节点中的术语,即可查看该术语对应的规则信息,包括:规则类型、规则名称、规则的分数。选择每种规则,即可查看与之匹配的文件数量
        • 文件分析报告(Data Analysis Report)
          • 可在Netwrix管理控制台上查看数据分析报告,对报告中的数据进行筛选和细化,以查询包含文件按照分类结果的分布状态。常用的报告有三种:文件分布地图(按分类和数据源分组统计),以及最近一周分类标签分配情况
          • 文件分布地图-按分类法分组统计
          • 文件分布地图-按数据源分组统计
          • 最近7天分类标签分配情况

    image-20210914162552914

    小结

    Netwrix作为全球500余家公司的数据安全治理供应商,实际数据分类、数据审计、数据安全功能远不止这些。Netwrix的数据分类工具作为数据安全的基础,提供了诸多参考方向,例如:无需单独部署客户端,使用一套服务器、一个WEB管理控制台的轻量化部署,即可完成数据分类全过程;可基于不同种类的分类数据源配置相应的分类配置项,为更精确的定位数据源提供支撑;使用预定义的数据合规分类法及其规则,满足国外对个人隐私数据识别的主流需求;使用多维度的象限统计图表,更直观地查看数据的分布情况。除此之外,Netwrix的数据审计和数据安全功能,能够提供以数据分类为基石、以用户实体行为分析UEBA(User and Entity Behavior Analytics)为核心的数据安全审计功能,最终形成数据防护流程体系。

    资料

    数据安全分级分类文档资料列表名称

    image-20210914145814534

    展开全文
  • 非常感谢举办方让我们学到了新知识,DataCon也是我比较喜欢和推荐的数据安全比赛,这篇文章2020年10月就进了我的草稿箱,但由于小珞珞刚出生,所以今天才发表,希望对您有所帮助!感恩同行,不负青春。

    这是作者2020年参加清华大学、Coremail、奇安信DataCon举办的比赛,主要是关于钓鱼和异常邮件识别研究。非常感谢举办方让我们学到了新知识,DataCon也是我比较喜欢和推荐的大数据安全比赛,这篇文章2020年10月就进了我的草稿箱,但由于小珞珞刚出生,所以今天才发表,希望对您有所帮助!感恩同行,不负青春。

    在这里插入图片描述

    微步情报局C&C资产进行拓线关联发现 “白象三代”组织在2019年期间用于钓鱼攻击的资产域名:ioa-cstnet.org。该域名在2019年3、4月期间曾被用于伪装成中国科学院计算机网路络信息中心管理员对该所人员发起鱼叉式攻击活动,试图窃取科研人员邮箱账密。钓鱼邮件如下所示,因此鱼叉式钓鱼攻击越来越多,其安全防御也非常重要。

    在这里插入图片描述

    展开全文
  • 当您考虑到诸如电力,饮用水,食物或药品之的重要东西被中断时会发生什么,您会明白为什么实施严格的网络安全措施如此重要。您应该考虑以下6种ICS安全最佳实践: 1.深入了解工业控制系统中的每个设备 完整的ICS...
  • 1010100 耕地1010101 水田1010102 水浇地1010103 旱地1010199 其他耕地1010200 园地1010201 果园1010202 茶园1010299 其他园地1010300 林地1010301 林地1010302 灌木林地1010399 其他林地1010400 草地1010401 天然...
  • 2020年开年,一场突如其来的病毒肺炎由武汉蔓延至全国,打乱了很多人的节奏。受疫情影响,全国人民被迫宅在家中,企业开启远程办公,学校远程上课。不得不说,在当前形势...相信很多朋友也听到过五网线、六类网线...
  • 软考高级系统架构设计师:五大类安全服务一、五大类安全服务二、认证服务三、访问控制服务四、数据机密性服务五、数据完整性服务、抗抵赖服务 一、五大类安全服务 认证服务 访问控制服务 数据机密性服务 数据完整...
  • 2021年安全类公众号合集

    千次阅读 2021-09-09 20:34:47
    零CERT 奇安信CERT 绿盟科技CERT 快识 3072 Xsafe n1nty lin先森 杂术馆 安全鸭 黑战士 T9Sec Bypass huasec XG小刚 SecWiki 404安全 安译Sec(已注销) 国产008 Kali笔记 边界安全 阿乐你好 乌雲安全 Hack之道 ...
  • 六大常见的电脑故障原因

    千次阅读 2021-07-27 10:17:20
    六大常见的电脑故障原因导语:电脑故障的原因很多种,今天小编就为大家整理了常见的六大故障原因!欢迎阅读,仅供参考,更多相关的知识,请关注CNFLA学习网的栏目!常见故障一:系统不认硬盘系统从硬盘无法启动,从A...
  • 工作区子系统 工作区子系统图 目的是实现工作区终端设备与水平子系统... 架空方式:如果建筑物之间本来电线杆,则投资成本是最低的,但它不能提供任何机械保护,因此安全性能较差,同时也会影响建筑物外观的美观性。
  • 原来如果某个部门业务系统数据发生篡改、破坏、泄露、非法获取、非法利用的安全事件影响的只是局部而现在则是整个企业数据资产层面,可见数据安全已经明确上升到国家安全、整个企业、组织或机构安全的最高层级
  • 即将到来的的毕业季,又将成百上千万的大学生走上工作岗位,那么我们即将毕业的大学生的那些...目前来说这两个绝对是学生报考比率最高的一个证书,而且部分高校将大学英语四级和毕业挂钩,考不过的话可能吗...
  • 七类线哪些特点?...七类和六类布线很多显著的差别,最明显的就是带宽,与四类、五、超五六类相比,七类具有更高的传输带宽(至少600MHz),六类信道提供了至少200MHZ的综合衰减对串扰比及整.
  • 《数据安全法》对金融科技企业数据合规工作的点影响 文/泰和泰律师事务所 陈福中、潘兴琦、刘若愚 引言 2021年6月10日,十三届全国人大常委会第二十九次会议表决通过了《中华人民共和国数据安全法》(下称“...
  • 恶意代码检测(1)恶意代码攻击溯源及恶意样本分析”,这篇文章将从案例的角度进行更深入的讲解,也感谢所有参考的安全大厂和大佬,正是因为他们,我们国家的网络安全保障! 二.常见APT组织的攻击案例 1.海莲花...
  • 逆向分析之OllyDbg调试INT3断点、反调试、硬件断点与内存断点 [系统安全] 二十五.WannaCry勒索病毒分析 (1)Python复现永恒之蓝漏洞实现勒索加密 [系统安全] 二十.WannaCry勒索病毒分析 (2)MS17-010漏洞利用及病毒...
  • 因为是对已病毒进行分析后拿到的特征,所以误报率低,也是各大安全厂商采用的技术。 缺点: 只能检测已知恶意代码。容易被免杀绕过。 最后,针对特征值检测技术,恶意软件如何对抗? 手工修改自身特征 首先,利用...
  • 2.1 安全性攻击 • 攻击,是指实体透过不同的手段或渠道,对另一实体或目标实施的任何非授权行为,其后果是导致对行为对象的伤害或破坏 • 安全则是指事物没有受到伤害或破坏,没有危险、危害或损失的自然状态 • ...
  • 这些基础性知识不仅和系统安全相关,同样与我们身边的APP、常用软件及操作系统紧密联系,希望这些知识对您有所帮助,更希望大家提高安全意识,安全保障任重道远。本文参考了《软件安全》视频、安全网站和参考文献中...
  • 高职单招13大类:1.商贸管理类、2.财经类、3.政法类、4.师范教育类、5.文秘类、6.工艺美术类、7.旅游类、8.机械类、9.计算机类、10.电子电气类、11.建筑类、12.农学类、13.医学类。单招考试准备工作大部分高职院校会...
  • 《当人工智能遇上安全》系列博客将详细介绍人工智能与安全相关的论文、实践,并分享各种案例,涉及恶意代码检测、恶意请求识别、入侵检测、对抗样本等等。前一篇文章普及了基于机器学习的恶意代码检测技术,主要参考...
  • 2021年全国大学生网络安全邀请赛暨第七届"东华杯"上海市大学网格全大赛Writeup Misc checkin 题目给了+AGYAbABhAGcAewBkAGgAYgBfADcAdABoAH0- 是UTF-7编码,解码得到flag flag为: flag{dhb_7th} project 下载附件...
  • 自动化专业属什么大类?属于电气信息类。自动化该专业是以自动控制理论为主要理论基础,以电子技术、计算机信息技术、传感器与检测技术等为主要技术手段,对各种自动化装置和系统实施控制。专业两个发展方向,第一...
  • 网络漏洞分类

    千次阅读 2021-07-22 11:46:36
    威胁信息安全漏洞划分为40种类型,分别是:缓冲区溢出、跨站脚本、DOS攻击、扫描、SQL注入、木马后门、病毒蠕虫、web攻击、僵尸网络、跨站请求伪造、文件包含、文件读取、目录遍历攻击、敏感信息泄露、暴力破解、...
  • 面向对象(1、三大特征;2、六大原则)

    千次阅读 多人点赞 2021-09-13 16:59:18
    不允许外部程序直接访问,而是通过该提供的方法来实现对隐藏信息的操作和访问 成员变量private,提供对应的getXxx()/setXxx()方法 3、封装好处 通过方法来控制成员变量的操作,提高了代码的安全性 把代码用方法...
  • 我们这里讨论的线程安全,限定于多个线程之间存在共享数据访问这个前提,因为如果一段代码根本不会与其他线程共享数据,那么从线程安全的角度来看,程序是串行执行还是多线程执行对它来说是完全没有区别的
  • 解读制造业数字化转型的六大因素

    千次阅读 2021-11-11 13:47:53
    三、企业利用专门信息安全技术保障网络环境、数据采集、系统集成等方面的可用性、完整性、保密性检测与管理。 3.资源数字化是企业数字化转型的必要路线 企业基于“云基础设施+云计算架构”搭建平台,推动生产全过程...
  • 本文对《大型互联网企业安全架构》里的核心内容做了梳理,并加入了深层解释。很适合安全小白入门企业安全
  • 2021年高考已经落下帷幕,大家也都查到了自己的分数,很多家长和学生都在翻阅着报考指南,选择自己喜欢的...2021软科财经大学排名,上海财经遥遥领先,东财仅排第提起财经院校,可能很多人第一时间就会想起...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 235,706
精华内容 94,282
关键字:

安全有哪六大类