线程的同步与死锁

作者: 江湖非良人 | 来源:发表于2019-07-22 15:29 被阅读47次

      在多线程的处理之中,可以利用Runnable描述多个线程操作的资源,而Thread描述每一个线程对象,当然多个线程访问同一资源时如果处理不当,就会产生数据的错误操作。

    同步问题的引出

      现在编写一个简单的卖票程序,将若干个线程对象实现卖票的处理操作。
    范例:实现卖票操作

    class MyThread implements Runnable {
        private int ticket = 10;//总票数为10张
        @Override
        public void run() {
            while (true) {
                if (this.ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket--);
                } else {
                    System.out.println("***** 票卖光了 *****");
                    break;
                }
            }
        }
    }
    public class ThreadDemo {
        public static void main(String[] args) throws Exception {
            MyThread mt=new MyThread();
            new Thread(mt,"票贩子A").start();
            new Thread(mt,"票贩子B").start();
            new Thread(mt,"票贩子C").start();
        }
    }
    

      此时的程序将创建3个线程对象,并且这三个线程将进行10张票的出售。此时的程序在进行卖票处理的时候,并没有任何的问题(假象),下面可以模拟一下卖票中的延迟操作。

    class MyThread implements Runnable {
        private int ticket = 10;//总票数为10张
        @Override
        public void run() {
            while (true) {
                if (this.ticket > 0) {
                    try {
                        Thread.sleep(100);//模拟网络延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket--);
                } else {
                    System.out.println("***** 票卖光了 *****");
                    break;
                }
            }
        }
    }
    

      这个时候追加了延迟问题就暴露出来了,而实际上这个问题一直都在。


    卖票处理

    线程同步

      经过分析之后已经可以确定同步问题产生的主要原因了,那么下面就需要进行同步问题的解决,但是解决问题的关键是锁,指的是当某一个线程执行操作的时候,其他线程外面等待;


    问题的解决

      如果想程序中实现锁功能,就可以使用synchronized关键字来是吸纳,利用synchronized可以定义同步方法和同步代码块,在同步代码块的操作中的代码只允许一个线程执行。
    1、利用同步代码块进行处理:

    synchronized (同步对象){
        同步代码操作;
    }
    

      一般要进行同步对象处理时,可以采用当前对象this进行同步。
      范例:利用同步代码块解决数据同步访问问题

    class MyThread implements Runnable {
        private int ticket = 1000;//总票数为10张
        @Override
        public void run() {
            while (true) {
                synchronized (this) {//每一次只允许一个线程进行访问
                    if (this.ticket > 0) {
                        try {
                            Thread.sleep(100);//模拟网络延迟
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket--);
                    } else {
                        System.out.println("***** 票卖光了 *****");
                        break;
                    }
                }
            }
        }
    }
    public class ThreadDemo {
        public static void main(String[] args) throws Exception {
            MyThread mt = new MyThread();
            new Thread(mt, "票贩子A").start();
            new Thread(mt, "票贩子B").start();
            new Thread(mt, "票贩子C").start();
        }
    }
    

      加入同步处理之后,程序的整体性能下降了。同步实际上会造成性能的降低
    2、利用同步方法解决:只需要在方法定义上使用synchronized关键字即可。

    class MyThread implements Runnable {
        private int ticket = 10;//总票数为10张
        public synchronized boolean sale() {
            if (this.ticket > 0) {
                try {
                    Thread.sleep(100);//模拟网络延迟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket--);
                return true;
            } else {
                System.out.println("***** 票卖光了 *****");
                return false;
            }
        }
        @Override
        public void run() {
            while (this.sale()) {}
        }
    }
    public class ThreadDemo {
        public static void main(String[] args) throws Exception {
            MyThread mt = new MyThread();
            new Thread(mt, "票贩子A").start();
            new Thread(mt, "票贩子B").start();
            new Thread(mt, "票贩子C").start();
        }
    }
    

      在日后学习Java类库时,系统中许多的类上使用的同步处理采用的都是同步方法。

    死锁

      死锁是在进行多线程同步的处理之中有可能产生的一种问题,所谓的死锁指的是若干个线程彼此互相等待的状态。

    public class DeadLock implements Runnable {
        private Producer producer = new Producer();
        private Customer customer = new Customer();
        public DeadLock() {
            new Thread(this).start();
            customer.say(producer);
        }
        public static void main(String[] args) {
            new DeadLock();
        }
        @Override
        public void run() {
            producer.say(customer);
        }
    }
    class Producer {
        public synchronized void say(Customer customer) {
            System.out.println("店员:先买单后吃饭");
            customer.get();
        }
        public synchronized void get() {
            System.out.println("收到钱,可以给你做饭了");
        }
    }
    class Customer {
        public synchronized void say(Producer producer) {
            System.out.println("顾客:先吃饭后买单");
            producer.get();
        }
        public synchronized void get() {
            System.out.println("吃饱饭了,可以买单了");
        }
    }
    

      现在死锁造成的主要原因是因为彼此都在互相等待着,等待着对方先让出资源。死锁实际上是一种开发中出现的不确定的状态,有时代码处理不当,则会不定期出现死锁,这属于正常开发中的调试问题。
      若干个线程访问同一资源时一定要进行同步处理,但过多的同步会造成死锁。

    相关文章

      网友评论

        本文标题:线程的同步与死锁

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