Java同步代码块与锁处理多线程安全问题
多线程问题分析:在Java中,如果多个线程同时对同一个共享资源进行修改或访问,就会出现竞态条件(Race Condition)问题。这种情况下,由于线程执行的顺序是不确定的,可能会导致结果与预期不符。例如,多个线程同时写入同一个文件,可能导致文件内容混乱。比如如下案例
需求:假如电影院共有100张电影票需要出售,在门口一共有三台售票机同时售票,请设计一款程序模拟售票
运行结果:
由此引发问题:同一张票被出售三次,如果按照这样计算,则整个程序会售出300张票,与需求不符
那么如何解决呢?采用同步机制避免该问题,此时我们就可以利用Java中的锁来解决,比如通常用的Synchronized来关键字声明方法或代码块等等...
一、Synchronized(同步锁)
在Java中,synchronized是一种锁机制。当一个代码块或方法被synchronized关键字修饰时,它就会对当前对象或指定的对象进行加锁操作。这样,在同一时刻只有一个线程可以访问该代码块或方法,其他线程将被阻塞,直到获取到锁之后才能够继续执行。Synchronized有两种修饰方式一种是修饰代码块,一种是修饰方法,下面我们来一一演示
1.同步代码块
语法:synchronized(锁对象){...........};
注意锁对象可以是任意Object对象,但是要确保唯一,见如下代码
运行结果:
2.同步方法
语法:就是把synchronized关键字加到方法上,执行时同步的是整个方法内的代码..如下图
运行结果:
总结:通过同步块或同步方法的方式使数据能够更安全的操作数据,避免多线程操作内容混乱的现象
二、Lock锁
Lock锁是Java中用于同步控制的接口,与synchronized关键字相比,Lock锁更加灵活,可以手动控制锁的获取和释放。与synchronized不同,使用Lock锁可以实现公平性、可重入性以及等待可中断等高级特性,提供了一种更为精细的多线程同步控制方式。那么如何实现Lock锁呢?
实现:
1.声明Lock对象,注意:Lock对象是一个接口,所以可以实例化他的实现类ReetrantLock
2.将以前使用Synchronized包裹的区域使用lock.lock()、lock.unlock()手动开启/关闭锁
3.由于锁开启后必定需要关闭,所以我们可以使用try-catch-finally将代码包裹,把手动关闭放入finnally中,即代码执行后始终执行关闭锁,具体代码如下:运行结果:
与Synchronized效果相同
Synchronized与Lock(ReetrantLock)锁有什么区别?
Synchronized更简洁但不灵活,给出关键字后,会在运行时自动开启锁运行完毕后会关闭锁无需手动添加代码
ReetrantLock更加灵活但相较复杂,Lock需要手动开启/关闭,并且大多数情况下关闭锁为了100%执行还要添加try-catch-finnally来保证,代码相较于Synchronized更复杂
扩展:死锁
死锁指的是两个或多个线程相互等待对方已持有的资源,造成了所有线程都阻塞无法继续执行的现象。在死锁情况下,需要手动中断程序才能解除阻塞状态。直接上案例:
运行结果
如上运行结果:线程1和线程2在相互等待对方释放锁,然后执行剩下的代码,此时程序尚未结束,形成假死状态,相互等待,这种现象就是死锁了,大多数死锁其实就是一个锁中嵌套了另一个锁,而内部的锁被其他锁占用未释放就会导致这种现象...
54
发表评论