美文网首页
JAVA多线程之线程安全

JAVA多线程之线程安全

作者: 逆水寻洲 | 来源:发表于2019-05-23 15:31 被阅读0次

    什么是线程安全

    当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是只做读操作是不会发生数据冲突问题。

    案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。

    /**
     * describe: 模拟多线程售票(未加锁,不安全)
     * @author dengzl
     * @date 2019/05/23
     */
    public class ThreadTrain implements Runnable {
    
        private int trainCount = 100;
    
        @Override
        public void run() {
            while (trainCount > 0) {
                try {
                    Thread.sleep(50);
                } catch (Exception e) {
                }
                sale();
            }
        }
        private void sale() {
            if (trainCount > 0) {
                System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
                trainCount--;
            }
        }
        public static void main(String[] args) {
            ThreadTrain threadTrain = new ThreadTrain();
            Thread t1 = new Thread(threadTrain, "①号");
            Thread t2 = new Thread(threadTrain, "②号");
            t1.start();
            t2.start();
        }
    }
    

    运行结果:
    一号窗口和二号窗口同时出售火车第99张,部分火车票会重复出售


    运行结果.png

    结论:多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。

    如何解决多线程之间线程安全问题?

    根据线程安全问题产生的条件,解决线程安全问题的思路是消除产生线程安全问题的环境:

    1. 消除共享数据:成员变量与静态变量多线程共享,将这些全局变量转化为局部变量,局部变量存放在栈,线程间不共享,就不存在线程安全问题产生的环境了。消除共享数据的不足:如果需要一个对象采集各个线程的信息,或者在线程间传递信息,消除了共享对象就无法实现此目的。
    2. 使用线程同步机制:给读写操作同时加锁,使得同时只有一个线程可以访问共享数据。同步机制的缺点是降低了程序的吞吐量。
    3. 建立副本:使用ThreadLocal为每一个线程建立一个变量的副本,各个线程间独立操作,互不影响。该方式本质上是消除共享数据思想的一种实现。

    使用synchronized和lock解决线程安全问题

    内置的锁(synchronized)

    Java提供了一种内置的锁机制来支持原子性。
    每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁。
    内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁。
    内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
    1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象。
    2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活。

    synchronized使用

    private synchronized void sale() {
            if (trainCount > 0) {
                System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
                trainCount--;
            }
        }
    

    Lock

    先来说说它跟synchronized有什么区别吧,Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,什么意思?就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。

    private  void  sale() {
            lock.lock();
            try {
                if (trainCount > 0) {
                    System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
                    trainCount--;
                }
            }finally {
                lock.unlock();
            }
        }
    

    运行结果:


    同步.png

    相关文章

      网友评论

          本文标题:JAVA多线程之线程安全

          本文链接:https://www.haomeiwen.com/subject/xagvzqtx.html