线程基础之多线程之间同步

一、目标

  1. 理解线程安全?
  2. synchronized用法
  3. 死锁

二、什么是线程安全

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

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

/**
 * @ Author        :  冯旭
 * @ Description   :  火车票线程
 * @ CreateDate    :  2020/1/8$ 17:08$
 * @ UpdateUser    :  修改人
 * @ UpdateDate    :  2020/1/8$ 17:08$
 * @ UpdateRemark  :  修改内容
 * @ Version       :  1.0
 * 〈坚持灵活 灵活坚持〉
 */
public class TreadDemo01 implements Runnable {
    private int count=100;
    @Override
    public void run() {
        while (count>0){
            try {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
                count--;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        TreadDemo01 treadDemo01 = new TreadDemo01();
        Thread thread01 = new Thread(treadDemo01);
        Thread thread03 = new Thread(treadDemo01);
        Thread thread02 = new Thread(treadDemo01);
        thread01.start();
        thread02.start();
        thread03.start();
    }
}

当多个线程同时进入循环时,循环还没有执行完,退出cpu,继续在竞争cpu的执行权。出现线程安全问题

三、 线程安全解决办法

如何解决多线程之间线程安全问题?
答:使用多线程之间同步或使用锁(lock)。

问:为什么使用线程同步或使用锁能解决线程安全问题呢?
答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。

问:什么是多线程之间同步?
答:当多个线程共享同一个资源,不会受到其他线程的干扰。

3.1 使用同步代码块

什么是同步代码块?
答:就是将可能会发生线程安全问题的代码,给包括起来。

/**
 * @ Author        :  冯旭
 * @ Description   :  线程火车票测试
 * @ CreateDate    :  2020/1/8$ 17:08$
 * @ UpdateUser    :  修改人
 * @ UpdateDate    :  2020/1/8$ 17:08$
 * @ UpdateRemark  :  修改内容
 * @ Version       :  1.0
 * 〈坚持灵活 灵活坚持〉
 */
public class TreadDemo01 implements Runnable {
    private int count=100;
    //自定义锁(必须共享同一个锁) 实例化
    private Object object = new Object();
    @Override
    public void run() {
            while (count>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //同步代码块
                synchronized (object){
                    if(count>0){
                        System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
                    }
                }
                count--;
            }
    }
}

四、 同步函数

什么是同步函数?
答:在方法上修饰synchronized 称为同步函数

/**
 * @ Author        :  冯旭
 * @ Description   :  线程火车票测试
 * @ CreateDate    :  2020/1/8$ 17:08$
 * @ UpdateUser    :  修改人
 * @ UpdateDate    :  2020/1/8$ 17:08$
 * @ UpdateRemark  :  修改内容
 * @ Version       :  1.0
 * 〈坚持灵活 灵活坚持〉
 */
public class TreadDemo01 implements Runnable {
    private int count=100;
    //自定义锁(必须共享同一个锁) 实例化
    private Object object = new Object();
    @Override
    public void run() {
            while (count>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sild();
                //同步代码块
               // synchronized (object){

               // }

            }
    }
    //同步函数
    public synchronized void sild(){
        if(count>0){
            System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
        }
        count--;
    }
}

同步函数用的是什么锁?
用的是this锁

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

public class Main {

    public static void main(String[] args) {
        TreadDemo01 treadDemo01 = new TreadDemo01();
        Thread thread01 = new Thread(treadDemo01);
        Thread thread03 = new Thread(treadDemo01);
        Thread thread02 = new Thread(treadDemo01);
        thread01.start();
        try {
            Thread.sleep(40);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        treadDemo01.flag=false;
        thread02.start();
        try {
            Thread.sleep(40);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        treadDemo01.flag=false;
        thread03.start();
    }
}

/**
 * @ Author        :  冯旭
 * @ Description   :  线程火车票测试
 * @ CreateDate    :  2020/1/8$ 17:08$
 * @ UpdateUser    :  修改人
 * @ UpdateDate    :  2020/1/8$ 17:08$
 * @ UpdateRemark  :  修改内容
 * @ Version       :  1.0
 * 〈坚持灵活 灵活坚持〉
 */
public class TreadDemo01 implements Runnable {
    private int count=100;
    public  boolean flag = true;
    //自定义锁(必须共享同一个锁) 实例化
    private Object object = new Object();
    @Override
    public void run() {
        if (flag) {
            while (true){

                //同步代码块
                synchronized (this){
                    if(count>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
                        count--;
                    }

                }

                //同步代码块
                // synchronized (object){
                // }
            }
        }else{
            while (true){
                sild();
            }
        }

    }
    //同步函数
    public synchronized void sild(){
        if(count>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
            count--;
        }

    }
}

五、 静态同步函数

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

/**
 * @ Author        :  冯旭
 * @ Description   :  线程火车票测试
 * @ CreateDate    :  2020/1/8$ 17:08$
 * @ UpdateUser    :  修改人
 * @ UpdateDate    :  2020/1/8$ 17:08$
 * @ UpdateRemark  :  修改内容
 * @ Version       :  1.0
 * 〈坚持灵活 灵活坚持〉
 */
public class TreadDemo01 implements Runnable {
    private int count=100;
    public  boolean flag = true;
    //自定义锁(必须共享同一个锁) 实例化
    private Object object = new Object();
    @Override
    public void run() {
//        if (flag) {
            while (true){
                //同步代码块
                synchronized (TreadDemo01.class){
                    if(count>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
                        count--;
                    }

                }

                //同步代码块
                // synchronized (object){
                // }
            }
//        }else{
//            while (true){
//                sild();
//            }
//        }

    }
}

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

六、 多线程死锁

什么是多线程死锁?
同步中嵌套同步,导致锁无法释放

/**
 * @ Author        :  冯旭
 * @ Description   :  线程火车票测试
 * @ CreateDate    :  2020/1/8$ 17:08$
 * @ UpdateUser    :  修改人
 * @ UpdateDate    :  2020/1/8$ 17:08$
 * @ UpdateRemark  :  修改内容
 * @ Version       :  1.0
 * 〈坚持灵活 灵活坚持〉
 */
public class TreadDemo01 implements Runnable {
    private int count=100;
    public  boolean flag = true;
    //自定义锁(必须共享同一个锁) 实例化
    private Object object = new Object();
    @Override
    public void run() {
       if (flag) {
            while (true){
                //同步代码块
                // 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.
                // 如果flag为true 先拿到 obj锁,在拿到this 锁、 才能执行。
                // 如果flag为false先拿到this,在拿到obj锁,才能执行。
                // 死锁解决办法:不要在同步中嵌套同步。
                synchronized (object){
                    sild();

                }
            }
        }else{
            while (true){
                sild();
            }
        }

    }
    //同步函数
    public synchronized void sild(){
        synchronized (object){
            if(count>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - count + 1) + "张票.");
                count--;
            }
        }
    }
}

七、 面试题

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

  2. 问:如何解决多线程之间线程安全问题?
    答:使用多线程之间同步或使用锁(lock)。

  3. 问:为什么使用线程同步或使用锁能解决线程安全问题呢?
    答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。被包裹的代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题.

  4. 问:什么是多线程之间同步?
    答:当多个线程共享同一个资源,不会受到其他线程的干扰。

  5. 问:什么是同步代码块?
    答:就是将可能会发生线程安全问题的代码,给包括起来。只能让当前一个线程进行执行,被包裹的代码执行完成之后才能释放所,让后才能让其他线程进行执行。

  6. 问:多线程同步的分类?
    a. 使用同步代码块。

    synchronized(同一个数据){
    可能会发生线程冲突问题
    }

    b. 使用同步函数
    在方法上修饰synchronized 称为同步函数。

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

  7. 问:同步代码块与同步函数区别?
    答: 同步代码使用自定锁(明锁) 同步函数使用this锁

  8. 问:同步函数与静态同步函数区别? (例如现在一个静态方法和一个非静态静态怎么实现同步?)
    同步函数使用this锁 静态同步函数使用字节码文件,也就是类.class

  9. 问:什么是多线程死锁?
    同步中嵌套同步
    解决办法:同步中尽量不要嵌套同步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值