多线程之间实现同步

一、什么是线程安全?

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

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

public class MainThread {
    public static void main(String[] args) {
        //  创建一个实例对象
        TrainThread trainThread = new TrainThread();
        Thread window1 = new Thread(trainThread, "窗口1");
        Thread window2 = new Thread(trainThread, "窗口2");
        window1.start();
        window2.start();

    }
}

class TrainThread implements Runnable {
	//	定义100张票
    private int count = 100;

    @Override
    public void run() {
        while (count > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "张火车票!");
            count--;
        }
    }
}

程序运行截图:
在这里插入图片描述
窗口1和窗口2同时出售第一张火车票,部分火车票会重复出售。
结论发现: 多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题

二、解决线程安全问题

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

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

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

2.1、使用同步代码块

问:什么是同步代码块?
就是将可能会发生线程安全问题的代码,给包括起来。
synchronized(同一个对象){
可能会发生线程冲突问题
}

class TrainThread implements Runnable {

    private int count = 100;

    @Override
    public void run() {
        while (count > 0) {
            synchronized (TrainThread.class){
                System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "张火车票!");
                count--;
            }
        }
    }
}
2.2、同步函数

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

public synchronized void sale() {
        if (count > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "张火车票!");
            count--;
        }
    }

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

2.3、静态同步函数

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

synchronized (TrainThread.class){
                System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "张火车票!");
                count--;
            }

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

三、多线程死锁

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

class TrainThread implements Runnable {
	// 这是货票总票数,多个线程会同时共享资源
	private int trainCount = 100;
	public boolean flag = true;
	private Object obj= new Object();

	@Override
	public void run() {
		if (flag) {
			while (true) {
				synchronized (obj) {
					// 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.
					// 如果flag为true 先拿到 obj锁,再拿到this 锁、 才能执行。
					// 如果flag为false先拿到this,再拿到obj锁,才能执行。
					// 死锁解决办法:不要在同步中嵌套同步。
					sale();
				}
			}
		} else {
			while (true) {
				sale();
			}
		}
	}

	/**
	 * 功能描述:(出售火车票)
	 */
	public synchronized void sale() {
		synchronized (obj) {
			if (trainCount > 0) {
				try {
					Thread.sleep(40);
				} catch (Exception e) {

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

四、面试中多线程问题总结
问题一:什么是多线程安全?

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

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

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

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

将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。被包裹的代码执行完成后释放锁,让后才能让其他线程进行执行,这样的话就可以解决线程不安全问题。

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

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

问题五:什么是同步代码块?

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

问题六:多线程同步的分类?

1.使用同步代码块?
synchronized(同一个数据){
可能会发生线程冲突问题
}
2.使用同步函数
在方法上修饰synchronized 称为同步函数
3静态同步函数
方法上加上static关键字,使用synchronized 关键字修饰 为静态同步函数
静态的同步函数使用的锁是 该函数所属字节码文件对象

问题七:同步代码块与同步函数区别?

同步代码使用自定锁(明锁)
同步函数使用this锁

问题八:同步函数与静态同步函数区别?

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

问题九:什么是多线程死锁?

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王盖茨666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值