目录
一、什么是同步
对在系统中所发生的事件之间进行协调,在时间上出现一致性与统一化的现象;
二、什么是加锁
1、加锁 > 修改 > 解锁 2、加锁机制可保障在任一时刻只有一个线程可以进入共享资源的代码区
三、加锁方式有
1、synchronized
* 同步代码块和同步方法执行结束,或者异常终止时,当前线程会自动解锁
01、同步代码块
synchronized(obj) {...} obj代表锁定的目标,(同步监视器),他可以是任意的对象; * 考虑到他的作用,建议采用并发访问资源作为同步监视器;
02、同步方法
public synchronized void fun() {...} 无需显示指定的同步监视器,在成员方法中默认为this,在静态方法中它默认为当前的类
03、synchronized的缺点
1、将加锁即解锁的过程固化,便捷性有余,但灵活性不足 2、某个线程在访问同步代码块时,被阻塞了,其他线程只能等待,影响程序的执行效率 3、在访问共享资源时,对多个线程同时读的场景,实际不会产生任何冲突,但是,synchronized不会区分这种场景,仍然做加锁处理,这是影响程序的执行效率
2、Lock
01、Lock(接口)
* 实现类是ReentrantLock(重入锁):即支持重新进入锁,表示该锁可以支持一个线程对资源重复加锁 1、void lock() 获得锁 2、void unlock() 释放锁 3、boolean tryLock(long time, TimeUnit unit) 获取锁,超时则返回false
02、ReadWriteLock(接口)
* 实现类是ReentrantReadWriteLock 1、Lock readLock() 返回用于读取的锁 2、Lock writeLock() 返回用于写入的锁
四、加锁代码示例
模拟简化的抢票业务场景
1、未加锁处理
public static void main(String[] args){
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task,"Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task,"John").start();
new Thread(task,"Lily").start();
new Thread(task,"张三").start();
new Thread(task,"李四").start();
}
//设计票数
private static class Ticket{
private int amount;//票数
public Ticket(int amount){//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount(){
return amount;
}
public void buy(int amount) {
if (this.amount < amount){
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}
}
//卖票
private static class buyTask implements Runnable{
private Ticket ticket;
public buyTask(Ticket ticket){//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
输出结果:不安全
John购买成功,剩余票数95
李四购买成功,剩余票数95
Lily购买成功,剩余票数95
Tom购买成功,剩余票数95
张三购买成功,剩余票数95
2、synchronized
01、同步代码块
//模拟抢票系统
public static void main(String[] args) {
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task, "John").start();
new Thread(task, "Lily").start();
new Thread(task, "张三").start();
new Thread(task, "李四").start();
}
//设计票数
private static class Ticket {
private int amount;//票数
public Ticket(int amount) {//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount() {
return amount;
}
public void buy(int amount) {
if (this.amount < amount) {
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//对之下代码加锁,解决数据共享时候的BUG
synchronized (this){
//通过校验成功购票
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}
}
}
//卖票
private static class buyTask implements Runnable {
private Ticket ticket;
public buyTask(Ticket ticket) {//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
输出结果:线程安全
张三购买成功,剩余票数99
John购买成功,剩余票数98
李四购买成功,剩余票数97
Tom购买成功,剩余票数96
Lily购买成功,剩余票数95Process finished with exit code 0
02、同步方法
此方法比同步代码块差,因为加锁范围大,加锁后性能降低
//模拟抢票系统
public static void main(String[] args) {
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task, "John").start();
new Thread(task, "Lily").start();
new Thread(task, "张三").start();
new Thread(task, "李四").start();
}
//设计票数
private static class Ticket {
private int amount;//票数
public Ticket(int amount) {//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount() {
return amount;
}
public synchronized void buy(int amount) {
if (this.amount < amount) {
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}
}
//卖票
private static class buyTask implements Runnable {
private Ticket ticket;
public buyTask(Ticket ticket) {//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
输出结果:线程安全
Tom购买成功,剩余票数99
李四购买成功,剩余票数98
张三购买成功,剩余票数97
Lily购买成功,剩余票数96
John购买成功,剩余票数95Process finished with exit code 0
3、同步锁
最优解,灵活性高,可以实现读写锁分离
//模拟抢票系统
public static void main(String[] args) {
Ticket ticket = new Ticket(100);//初始化100张票
//售卖系统
buyTask task = new buyTask(ticket);
new Thread(task, "Tom").start();//三者共享同一个数据,共享数据场景
new Thread(task, "John").start();
new Thread(task, "Lily").start();
new Thread(task, "张三").start();
new Thread(task, "李四").start();
}
//设计票数
private static class Ticket {
private Lock lock = new ReentrantLock();//实例化锁对象
private int amount;//票数
public Ticket(int amount) {//构造器初始化票数
this.amount = amount;
}
//访问数量的方法
public int getAmount() {
return amount;
}
public void buy(int amount) {
if (this.amount < amount) {
throw new IllegalArgumentException("余量不足");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();//表示在此处加锁
try{
this.amount -= amount;
String name = Thread.currentThread().getName();
System.out.println(name + "购买成功,剩余票数" + this.amount);
}finally {
lock.unlock();//解锁
}
}
}
//卖票
private static class buyTask implements Runnable {
private Ticket ticket;
public buyTask(Ticket ticket) {//构造器
this.ticket = ticket;
}
@Override
public void run() {
ticket.buy(1);//每次只能买一张
}
}
输出结果:线程安全
Tom购买成功,剩余票数99
Lily购买成功,剩余票数98
John购买成功,剩余票数97
张三购买成功,剩余票数96
李四购买成功,剩余票数95Process finished with exit code 0