java线程同步
多个线程操作同一资源
线程同步其实就是一种排队等待机制
同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。 *解说:甲乙丙丁排队去卫生间,甲进入卫生间后上锁(加锁确保安全),乙丙丁继续排队(等待),等卫生间门打开甲出来后(释放),乙才能进去。
存在的问题:(安全和性能 不可兼得)
-
一个线程持有锁会导致其他所有需要此锁的线程挂起
-
在多线程竞争下,加锁,释放锁会导致较多的上下文切换 和 调度延时,引起性能问题
-
如果一个优先级高的线程等待一个优先级低的线程,会导致优先级倒置,引起性能问题
*性能倒置,解说:甲乙排队去卫生间,甲需要占用30分钟,而乙只需要占用2分钟,如果先让甲使用,乙就需要多等30分钟。
synchronized 两种使用方式
-
同步方法:public synchronized void method(){}
-
同步块:synchronized (obj) { }
-
obj称为同步监视器,可以是任何对象,推荐使用共享资源作为同步监视器
-
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this对象本身,或者是class(反射)
-
注意: 使用synchronized时,一定要注意同步监视器指向谁,指向的对象是需要增删改的对象才可生效
// 多线程抢票例子
public class UnSaftBuyTicket {
public static void main(String[] args) {
// 在不加安全锁时,可能会出现两人同时抢到同一张票,或出现第0,-1张票。加锁后则不会出现
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"小明").start();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"小白").start();
}
}
class BuyTicket implements Runnable{
private Integer ticketNums = 10;// 票数
boolean flag = true;// 线程停止标志
死锁
多个线程占着对方需要的资源不放,形成僵持
产生死锁的四个必要条件:(只要避免下面任意一条件,即可避免死锁)
-
互斥条件:一个资源每次只能被一个进程使用(一个资源不能被多个进程同时使用)
-
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放(占用资源不放,导致其他进程一直等待)
-
不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺(某一资源被占用时,其他进程无法使用)
-
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(进程所需的资源都被占用,一直处于循环等待状态)
// 死锁,例子:两人化妆 竞争口红和镜子
public class TestDeadLock {
public static void main(String[] args) {
// 执行结果会发现,小红一直占用着口红永远无法拿到镜子,而小丽一直占用着镜子永远无法拿到口红,形成了僵持
Makeup p1 = new Makeup(0,"小红");
Makeup p2 = new Makeup(1,"小丽");
p1.start();
p2.start();
}
}
// 口红
class LipStick{ }
// 镜子
class Mirror{ }
// 化妆
class Makeup extends Thread{
// 通过static的方式确保口红和镜子只有一个
static LipStick lipStick = new LipStick();
static Mirror mirror = new Mirror();
int choose; // 选择 0口红,1镜子
String username; // 用户
public Makeup(int choose,String username){
this.choose = choose;
this.username = username;
}
Lock锁
Lock锁是JDK5.0提供的更强大的线程同步机制
ReentrantLock类(可重入锁)实现了Lock,它拥有与synchronized相同的并发性和内存语义
//JUC Lock锁
public class TestLock {
public static void main(String[] args) {
GetTicket getTicket = new GetTicket();
// 在不加安全锁时,可能会出现两人同时抢到同一张票,或出现第0,-1张票。加锁后则不会出现
new Thread(getTicket,"小明").start();
new Thread(getTicket,"小王").start();
}
}
class GetTicket implements Runnable{
int ticketNum = 10; // 票数
private final ReentrantLock lock = new ReentrantLock();
synchronized 与 Lock 的对比
-
Lock是显式锁(需先创建ReentrantLock对象,再手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放
-
-

浙公网安备 33010602011771号