Java小白入门 —— 多线程之间实现同步(二)

Java小白入门 —— 多线程之间实现同步(二)

一. 什么是线程安全:

为什么会存在线程安全问题:

当多个线程同时共享同一全局变量或静态变量时,在做读操作时不会发送数据冲突,而在做写操作时可能会发送数据冲突问题,就会出现线程安全问题。

二. 案例:

背景: 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。

代码如下:

/**
 * @author MuXin
 * @date 2020/10/21 16:11
 *
 *  某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
 */
public class ThreadDemo {

    public static void main(String[] args) {
        ThreadGoods threadGoods = new ThreadGoods();
        Thread threadA = new Thread(threadGoods, "A公司");
        Thread threadB = new Thread(threadGoods, "B公司");
        Thread threadC = new Thread(threadGoods, "C公司");
        threadA.start();
        threadB.start();
        threadC.start();
    }

    static class ThreadGoods implements Runnable {

        //货物总量
        private int goods = 100;

        @Override
        public void run() {
            while (goods > 0) {
                try {
                    //等待0.1s
                    Thread.sleep(100);
                } catch (Exception e) {
                }
                //执行运货操作
                System.out.println(Thread.currentThread().getName()+",搬运第"+(101-goods)+"T货物");
                goods--;
            }
        }
    }

}

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

三. 线程安全解决方案:

1. 使用同步代码块

将可能出现问题的代码块包裹起来。

/**
 * @author MuXin
 * @date 2020/10/21 16:11
 * <p>
 * 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
 */
public class ThreadDemo {

    public static void main(String[] args) {
        ThreadGoods threadGoods = new ThreadGoods();
        Thread threadA = new Thread(threadGoods, "A公司");
        Thread threadB = new Thread(threadGoods, "B公司");
        Thread threadC = new Thread(threadGoods, "C公司");
        threadA.start();
        threadB.start();
        threadC.start();
    }

    static class ThreadGoods implements Runnable {
        //自定义多线程同步锁
        private Object mutex = new Object();

        //货物总量
        private int goods = 100;

        @Override
        public void run() {

            while (goods > 0) {
                try {
                    //等待0.1s
                    Thread.sleep(100);
                } catch (Exception e) {
                }

                synchronized (mutex) {
                    if(goods > 0) {
                        try {
                            //等待0.1s
                            Thread.sleep(100);
                        } catch (Exception e) {

                        }
                        //执行运货操作
                        System.out.println(Thread.currentThread().getName() + ",搬运第" + (101 - goods) + "T货物");
                        goods--;
                    }
                }

            }
        }
    }
}

运行结果:
代码运行结果

2. 使用同步函数(synchronized锁)

在方法上修饰synchronized称为同步函数;
synchronized使用的锁是this锁。

/**
 * @author MuXin
 * @date 2020/10/21 16:11
 * <p>
 * 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
 */
public class ThreadGoods implements Runnable {
    //货物总量
    private int goods = 100;
    //自定义多线程同步锁
    private Object mutex = new Object();

    @Override
    public void run() {

        while (goods > 0) {
            try {
                //等待0.1s
                Thread.sleep(100);
            } catch (Exception e) {
            }
            moveGoods();
        }
    }

    public synchronized void moveGoods() {
        if (goods > 0) {
            try {
                //等待0.1s
                Thread.sleep(100);
            } catch (Exception e) {

            }
            //执行运货操作
            System.out.println(Thread.currentThread().getName() + ",搬运第" + (101 - goods) + "T货物");
            goods--;
        }
    }
}

执行结果:
执行结果

3. 静态同步函数

在方法上加static关键字,使用synchronized关键词修饰,或者使用类.class文件;
静态同步函数使用的锁是该函数所属字节码文件对象。

/**
 * @author MuXin
 * @date 2020/10/21 16:11
 * <p>
 * 某工厂需拖运100T货物,找了3家托运公司,用多线程模拟托运情况。
 */
public class ThreadGoods implements Runnable {
    //货物总量
    private int goods = 100;
    //自定义多线程同步锁
    private Object mutex = new Object();

    @Override
    public void run() {

        while (goods > 0) {
            try {
                //等待0.1s
                Thread.sleep(100);
            } catch (Exception e) {
            }
            moveGoods();
        }
    }

    public  void moveGoods() {
        synchronized (ThreadGoods.class) {
            if (goods > 0) {
                try {
                    //等待0.1s
                    Thread.sleep(100);
                } catch (Exception e) {

                }
                //执行运货操作
                System.out.println(Thread.currentThread().getName() + ",搬运第" + (101 - goods) + "T货物");
                goods--;
            }
        }
    }
}

执行结果:
执行结果

四. 多线程死锁:

1. 什么是多线程死锁:

在同步中嵌套同步,俗称套娃,导致锁无法释放。

2. 如何避免死锁:

1. 加锁顺序(线程按照一定的顺序加锁)
2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3. 死锁检测

五. 常见面试题:

1. 什么是线程安全

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

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

1. 使用同步代码块;
2. 使用 synchronized 锁;
3. 使用静态同步函数。

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

当会发生数据冲突问题时,使得同一时间只能执行一个线程,其他线程处于阻塞状态,在执行完之后释放锁,交给下一个线程执行。

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

在多线程情况下,当多个线程共享统一资源时,不会受到其他线程的影响。	

5. 什么是同步代码块?

就是将可能出现线程安全的代码包裹起来,使得同一时间只能有一个线程执行被包裹的代码,其他线程处于阻塞状态。	

6. 同步代码块和同步函数的区别?

同步代码块是自定义锁(明锁)
同步函数是使用的 this 锁

7. 同步函数和静态同步函数的区别?/ 例如现在一个静态方法和一个非静态静态怎么实现同步?

同步函数使用的 this 锁;
静态同步函数使用的是字节码文件,即类.class

8. 什么是死锁?

同步中嵌套同步,俗称套娃。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值