美文网首页
Java线程安全问题三种解决方法

Java线程安全问题三种解决方法

作者: 奔跑小电驴 | 来源:发表于2020-02-18 17:30 被阅读0次

    场景:抢票

    抢票的核心问题就是共享数据,多个人可以理解为多个线程,同时抢票;票数是共享数据,所有人都只能从唯一的余票池里抢票

    方式一:同步代码块

    synchronized(同步监视器){
      //需要被同步的代码
    }
    

    说明:
    1.操作共享数据的代码,即为需要被同步的代码
    2.共享数据:多个线程共同操作的变量,比如:ticket就是共享数据
    3.同步监视器,俗称锁,任何一个类的对象都可以充当锁,

    a) 基于Thread类继承实现的代码块同步

    public class ThreadDemo01{
        public static void main(String[] args) {
            Windows w1 = new Windows();
            Windows w2 = new Windows();
            Windows w3 = new Windows();
            w1.setName("W1");
            w2.setName("W2");
            w3.setName("W3");
            w1.start();
            w2.start();
            w3.start();
        }
    }
    
    class Windows extends Thread{
        private static int TICKET=100;
        private static Object obj = new Object();
    
        @Override
        public void run() {
            while (true){
                synchronized (Window.class) { //这个也一样,Window.class也是代表Window这个类
                //synchronized (obj) {    --这种是大家最常用的方法,前面new好的对象,这里引用
                    if (TICKET > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(getName() + ":" + TICKET);
                        TICKET--;
                    } else {
                        break;
                    }
                }
            }
        }
    }
    

    b) 基于实现Runnable接口的代码块同步

    public class ThreadDemo02{
        public static void main(String[] args) {
            Windows w = new Windows();
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
            t1.setName("T1");
            t2.setName("T2");
            t3.setName("T3");
            t2.start();
            t1.start();
            t3.start();
        }
    }
    
    class Windows implements Runnable{
        private int TICKET=100;
        @Override
        public void run() {
            while (true) {
                //this是代表Window1,因为在main里面,Window1只new了一次,所以这个this代表的对象唯一,故也可以作为锁来使用
                synchronized(this){
                    if (TICKET > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ":" + TICKET);
                        TICKET--;
                    }else {
                        break;
                    }
                }
            }
        }
    }
    

    方式二: 同步方法

    说明:同步方法实际上就是将需要同步的代码单独写成一个方法,将整个方法进行同步即可,

    a) 实现Runnable接口的方法同步

    public class ThreadDemo03{
        public static void main(String[] args) {
            Windows w = new Windows();
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
            t1.setName("T1");
            t2.setName("T2");
            t3.setName("T3");
            t2.start();
            t1.start();
            t3.start();
        }
    }
    
    class Windows implements Runnable{
        private int TICKET=100;
        @Override
        public void run() {
            while (true) {
                show();
            }
        }
    
        private synchronized void show(){ //同步监视器默认是this
            if (TICKET > 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + TICKET);
                Thread.yield();
                TICKET--;
            }
        }
    }
    

    b) 继承Thread类的子类的方法同步

    public class ThreadDemo04{
        public static void main(String[] args) {
            Windows w1 = new Windows();
            Windows w2 = new Windows();
            Windows w3 = new Windows();
            w1.setName("W1");
            w2.setName("W2");
            w3.setName("W3");
            w1.start();
            w2.start();
            w3.start();
    
        }
    }
    
    class Windows extends Thread{
        private static int TICKET=100;
    
        @Override
        public void run() {
            while (true){
                show();
            }
        }
    
        public static synchronized void show(){
            if (TICKET > 0) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + TICKET);
                TICKET--;
            }
        }
    }
    

    方式三

    Lock锁方式解决线程安全问题

    public class ThreadDemo05 {
        public static void main(String[] args) {
            Windows w = new Windows();
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    class Windows implements Runnable{
        private int TICKET=100;
        //1.实例化ReentrantLock
        private ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            while (true){
                try {
                    //2.调用lock()
                    lock.lock();
                    if (TICKET > 0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ":" + TICKET);
                        TICKET--;
                    }else {
                        break;
                    }
                } finally {
                    //3.解锁
                    lock.unlock();
                }
            }
        }
    }
    
    1. 线程安全的问题,最核心的问题就是对共享数据的操作,最核心的处理办法就是禁止多个线程同时操作共享数据,类似synchronized等其实也就是将多个线程的操作还是串行着去执行的,所以做了同步机制的代码执行效率就比没有的慢
    2. 演示代码是比较基础的,很多线程安全问题还是挺隐蔽的,类似死锁也是很隐蔽的,跟Demo级别的代码比起来还是很复杂的,需要不断总结和多练习才能搞定啦

    相关文章

      网友评论

          本文标题:Java线程安全问题三种解决方法

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