(二)多线程之间实现同步

多线程之间实现同步


学习目标

有明确的学习目标,学起来会更有趣哦:

  • 理解线程安全
  • synchronized用法
  • 死锁
  • 多线程创建方式
  • 练习题
  • 线程生命周期
  • 面试总结

一、什么是线程安全?

为什么有线程安全问题?

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

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

代码:

/**
 *多线程之买火车票案例-展示线程不安全
 */
class ThreadTrain1 implements Runnable {
    // 这是货票总票数,多个线程会同时共享资源
    private int trainCount = 100;

    @Override
    public void run() {
        while (trainCount > 0) {// 循环是指线程不停的去卖票
            try {
                // 等待100毫秒
                Thread.sleep(10);
            } catch (InterruptedException e) {

            }

            sale();
        }
    }

    /**
     * 出售火车票
     */
    public void sale() {
        if (trainCount > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {

            }
                          System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
            trainCount--;       }
    }
}

public class ThreadDemo2 {

    public static void main(String[] args) {

        ThreadTrain1 threadTrain = new ThreadTrain1(); // 定义 一个实例
        Thread thread1 = new Thread(threadTrain, "一号窗口");
        Thread thread2 = new Thread(threadTrain, "二号窗口");
        thread1.start();
        thread2.start();
    }

}

运行结果: 
这里写图片描述 
注意:一号窗口和二号窗口同时出售火车第一张和第七张,部分火车票会重复出售。

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


二、线程安全解决办法

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

答:使用多线程之间同步或使用锁(lock)。

问:为什么使用线程同步或使用锁能解决线程安全问题呢?

答:当使用线程同步或使用锁时,只能让当前一个线程进行执行;代码执行完成后释放锁,然后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。

问:什么是多线程之间同步?

答:当多个线程共享同一个资源,不会受到其他线程的干扰。

使用同步代码块

问:什么是同步代码块?

答:就是将可能会发生线程安全问题的代码,给包括起来。 
synchronized(同一个数据){ 
可能会发生线程冲突问题 
}

代码示例:

  • private Object mutex = new Object();// 自定义多线程同步锁
        public void sale() {
            synchronized (mutex) {
                if (trainCount > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                    }
                    System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
                    trainCount--;           }
            }
        }


同步函数

问:什么是同步函数?

答:在方法上修饰 synchronized 称为同步函数 
synchronized(同一个数据){ 
可能会发生线程冲突问题 
}

代码示例:

  • public synchronized void sale() {
                if (trainCount > 0) { 
                    try {
                        Thread.sleep(40);
                    } catch (Exception e) {
                    }
                    System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
                    trainCount--;
                }
        }


问:同步函数用的是什么锁?

答:同步函数使用this锁。 
证明方式: 一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,那么会出现数据错误。

代码 :

  • class ThreadTrain5 implements Runnable {
        // 这是货票总票数,多个线程会同时共享资源
        private int trainCount = 100;
        public boolean flag = true;
        private Object mutex = new Object();
    
        @Override
        public void run() {
            if (flag) {
                while (true) {
                    synchronized (mutex) {
                        if (trainCount > 0) {
                            try {
                                Thread.sleep(40);
                            } catch (Exception e) {
    
                            }
                            System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
                            trainCount--;
                        }
                    }
                }
            } else {
                while (true) {
                    sale();
                }
            }
        }
    
    
        public synchronized void sale() {
            if (trainCount > 0) {
                try {
                    Thread.sleep(40);
                } catch (Exception e) {
    
                }
                System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
                trainCount--;
            }
        }
    }
    
    public class ThreadDemo5 {
    
        public static void main(String[] args) throws InterruptedException {
    
            ThreadTrain5 threadTrain = new ThreadTrain5(); // 定义 一个实例
            Thread thread1 = new Thread(threadTrain, "一号窗口");
            Thread thread2 = new Thread(threadTrain, "二号窗口");
            thread1.start();
            Thread.sleep(40);
            threadTrain.flag = false;
            thread2.start();
        }
    
    }


静态同步函数

问:什么是静态同步函数?

答:方法上加上static关键字,使用synchronized 关键字修饰 或者使用类.class文件。 
静态的同步函数使用的锁是 该函数所属字节码文件对象 , 
可以用 getClass方法获取,也可以用当前 类名.class 表示。

代码示例:

  • //使用ThreadTrain.class锁
    synchronized (ThreadTrain.class) {
                System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
                trainCount--;
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                }
    }


总结

synchronized 修饰方法使用锁是当前this锁。 
synchronized 修饰静态方法使用锁是当前类的字节码文件。


三、多线程死锁

什么是多线程死锁?

答:同步中嵌套同步,导致锁无法释放!

代码:

  • class ThreadTrain6 implements Runnable {
        // 这是货票总票数,多个线程会同时共享资源
        private int trainCount = 100;
        public boolean flag = true;
        private Object mutex = new Object();
    
        @Override
        public void run() {
            if (flag) {
                while (true) {
                    synchronized (mutex) {
                        // 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.
                        // 如果flag为true 先拿到 obj锁,再拿到this 锁、 才能执行。
                        // 如果flag为false先拿到this,再拿到obj锁,才能执行。
                        // 死锁解决办法:不要在同步中嵌套同步。
                        sale();
                    }
                }
            } else {
                while (true) {
                    sale();
                }
            }
        }
    
        /**
         *出售火车票
         */
        public synchronized void sale() {
            synchronized (mutex) {
                if (trainCount > 0) {
                    try {
                        Thread.sleep(40);
                    } catch (Exception e) {
    
                    }
                    System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
                    trainCount--;
                }
            }
        }
    }
    
    public class DeadlockThread {
    
        public static void main(String[] args) throws InterruptedException {
    
            ThreadTrain6 threadTrain = new ThreadTrain6(); // 定义 一个实例
            Thread thread1 = new Thread(threadTrain, "一号窗口");
            Thread thread2 = new Thread(threadTrain, "二号窗口");
            thread1.start();
            Thread.sleep(40);
            threadTrain.flag = false;
            thread2.start();
        }
    
    }



四、面试题

1、什么是多线程安全?

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

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

答:使用多线程之间同步或使用锁(lock)。

3、为什么使用线程同步或使用锁能解决线程安全问题呢?

答:当使用线程同步或使用锁时,只能让当前一个线程进行执行;代码执行完成后释放锁,然后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。

4、什么是多线程之间同步?

答:当多个线程共享同一个资源,不会受到其他线程的干扰。

5、什么是同步代码块?

答:就是将可能会发生线程安全问题的代码,给包括起来。只能让当前一个线程进行执行,被包裹的代码执行完成之后才能释放所,然后才能让其他线程进行执行! 
synchronized(同一个数据){ 
可能会发生线程冲突问题 
}

6、如何使用同步函数?

答:在方法上修饰synchronized 称为同步函数。

7、如何使用静态同步函数?

答:方法上加上static关键字,使用synchronized 关键字修饰 为静态同步函数,静态的同步函数使用的锁是 该函数所属字节码文件对象。

8、同步代码块与同步函数区别?

答: 
1、同步代码块使用自定义锁(明锁) 
2、同步函数使用this锁

9、同步函数与静态同步函数区别?

注意:有些面试会这样问:例如现在一个静态方法和一个非静态静态怎么实现同步? 
答: 
1、同步函数使用this锁 
2、静态同步函数使用字节码文件,也就是类.class 
**

10、什么是多线程死锁?

答:同步中嵌套同步 
解决办法:同步中尽量不要嵌套同步

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值