美文网首页
Java-多线程详解(二)

Java-多线程详解(二)

作者: 一只洁_ | 来源:发表于2019-08-19 19:04 被阅读0次

    线程的同步

    1.线程同步问题的引出

    下面模拟一个简单的卖票程序,两个线程,卖10张票

    public class MyClass {
        public static void main(String[] args){
            Ticket ticket1 = new Ticket("线程A");
            Thread t1 = new Thread(ticket1);
    
            Ticket ticket2 = new Ticket("线程B");
            Thread t2 = new Thread(ticket2);
    
            t1.start();
            t2.start();
        }
    }
    
    //用于卖票的任务
    class Ticket implements Runnable{
        //定义所有车票的数量
        public static int num = 10;
        String name;
    
        public Ticket(String name){
            this.name = name;
        }
    
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                    //判断有没有票
                    if (num > 0) {
                        System.out.println(name + "出票:" + num--);
                    }
                }
            }
        }
    

    运行结果:

    线程B出票:10
    线程A出票:9
    线程B出票:8
    线程A出票:7
    线程A出票:5
    线程A出票:4
    线程A出票:3
    线程B出票:6
    线程B出票:1
    线程A出票:2
    

    这时我们发现,操作的结果A、B两个线程余票没有同步。那么,到底是如何造成这种不同步的呢?

    卖票的过程分为两个步骤:
    第一步:判断是否还有剩余的票数;
    第二步:票数减一

    2.线程同步问题的解决

    (1)同步代码块,通过synchronized关键词解决。同步代码块格式:

    synchronized(同步对象){
        需要同步的代码
    }
    

    在进行同步的操作时必须设置一个要同步的对象,任意对象我们用Object设置

        class Ticket implements Runnable{
        //定义所有车票的数量
        public static int num = 10;
        String name;
    
        public Ticket(String name){
            this.name = name;
        }
    
        Object object = new Object();
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
    
                synchronized (object){
                    //判断有没有票
                    if (num > 0) {
                        System.out.println(name + "出票:" + num--);
                    }
                }
            }
        }
    }
    

    发现结果还是不正确,这里我们需要主要,任何一个对象都有自己的一把锁,如果多个线程操作同一个代码块,并且需要同步,那么必须操作同一个对象/同一个对象的同一把锁,于是我们

    static final Object object = new Object();
    

    这时候我们再运行:

    线程A出票:10
    线程B出票:9
    线程B出票:8
    线程B出票:7
    线程B出票:6
    线程B出票:5
    线程B出票:4
    线程B出票:3
    线程B出票:2
    线程B出票:1
    

    运行多次会发现,AB没有交替出票,所以我们要用一个方法使两个线程交替执行

    synchronized (object){
                //判断有没有票
                if (num > 0) {
                    try {
                        //唤醒同步监听器监听其他线程
                        object.notify();
                        //让当前线程等待
                        object.wait();
    
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(name + "出票:" + num--);
                }
            }
    

    这样执行结果:

    线程A出票:10
    线程B出票:9
    线程A出票:8
    线程B出票:7
    线程A出票:6
    线程B出票:5
    线程A出票:4
    线程B出票:3
    线程A出票:2
    线程B出票:1
    

    (2)同步方法,使用synchronized关键字将一个方法声明成同步方法。定义格式:

    synchronized 方法返回值 方法名称(参数列表){   }
    

    具体如下:

    //用于卖票的任务
    class Ticket implements Runnable {
        //定义所有车票的数量
        public static int num = 10;
        String name;
    
        public Ticket(String name) {
            this.name = name;
        }
    
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                this.sale();
            }
        }
    
        public synchronized void sale(){
            //判断有没有票
            if (num > 0) {
                System.out.println(name + "出票:" + num--);
            }
        }
    }
    

    但其实这样是错误的,我们定义的synchronized方法 其实相当于

    synchronzied(this){}
    

    但是在执行类里面 我new了两个线程出来,this就不知道到底指哪一个,因为我定义的票数不多,看不出问题,所以建议自己尝试的时候把票数换成100,错误就会很明显了,因为要让程序正确,我们应该这样做

    public class MyClass {
        public static void main(String[] args){
            Ticket ticket = new Ticket();
    
            new Thread(ticket,"线程A").start();
            new Thread(ticket,"线程B").start();
        }
    }
    
     //用于卖票的任务
    class Ticket implements Runnable {
        //定义所有车票的数量
        public static int num = 10;
    
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                this.sale();
            }
        }
    
        public synchronized void sale(){
            //判断有没有票
            if (num > 0) {
                System.out.println(Thread.currentThread().getName()+ "出票:" + num--);
            }
        }
    }
    

    注意:同步方法必须确保多个对象调用的同步方法是操作的同一个对象

    (3)通过锁(Lock)解决

    public class MyClass {
        public static void main(String[] args){
            Ticket ticket1 = new Ticket("线程A");
            Thread t1 = new Thread(ticket1);
    
            Ticket ticket2 = new Ticket("线程B");
            Thread t2 = new Thread(ticket2);
    
            t1.start();
            t2.start();
        }
    }
    
    class Ticket implements Runnable{
        //定义所有车票的数量
        public static int num = 10;
        String name;
    
        public Ticket(String name){
            this.name = name;
        }
    
        static ReentrantLock reentrantLock = new ReentrantLock();
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                reentrantLock.lock();
                    //判断有没有票
                    if (num > 0) {
                        System.out.println(name + "出票:" + num--);
                    }
                reentrantLock.unlock();
            }
        }
    }
    

    运行结果:

    线程A出票:10
    线程A出票:9
    线程A出票:8
    线程B出票:7
    线程B出票:6
    线程B出票:5
    线程B出票:4
    线程B出票:3
    线程B出票:2
    线程B出票:1
    

    相关文章

      网友评论

          本文标题:Java-多线程详解(二)

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