传统的synchronized锁
并发操作(多线程操作同一个资源),正常代码
synchronized修饰同步代码块默认锁的是this,也可以是具体类的class(Ticket.class)
package com.wyh.demo; /** * @program: JUC * @description: 卖票 使用synchronized修饰方法或者代码块使得线程安全 * @author: 魏一鹤 * @createDate: 2022-02-11 22:31 **/ //真正的多线程开发,公司中的开发,降低耦合性 //线程就是一个单独的资源类,没有任何附属的操作 //1 属性 2 方法 public class saleTicketDemo01 { public static void main(String[] args){ //资源类 Ticket ticket=new Ticket(); //多线程操作买票 并发:多个线程操作多个资源类,把资源类丢入线程就可以了 //FunctionalInterface 函数式接口 jdk1.8 lambda表达式 (参数)->{代码} new Thread(()->{ for (int i = 0; i < 10; i++) { //调用卖票方法 ticket.sale(); } },"线程A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { //调用卖票方法 ticket.sale(); } },"线程B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { //调用卖票方法 ticket.sale(); } },"线程C").start(); } } //资源类 OOP编程 class Ticket { //属性 方法 //票数 private int ticketNums=10; //卖票方法 public void sale(){ //如果票数大于0就可以正常卖票 if(ticketNums > 0){ System.out.println(Thread.currentThread().getName() + "买到了"+ticketNums--+"张票"+"剩余:"+ticketNums+"张票"); } } }
通过控制台可以发现结果很混乱不是我们想要的 CPU的调度很混乱,资源被乱抢
线程A买到了10张票剩余:9张票
线程B买到了9张票剩余:8张票
线程A买到了8张票剩余:7张票
线程A买到了6张票剩余:5张票
线程B买到了7张票剩余:6张票
线程B买到了4张票剩余:3张票
线程B买到了3张票剩余:2张票
线程B买到了2张票剩余:1张票
线程B买到了1张票剩余:0张票
线程A买到了5张票剩余:4张票
被synchronized修饰方法和代码块 代码如下
class Ticket { //修饰方法 //synchronized本质就是队列,锁(排队) public synchronized void sale(){ //如果票数大于0就可以正常卖票 if(ticketNums > 0){ System.out.println(Thread.currentThread().getName() + "买到了"+ticketNums--+"张票"+"剩余:"+ticketNums+"张票"); } } //synchronized本质就是队列,锁(排队) //修饰代码块对象为this public void sale(){ //默认对象是this synchronized (this){ //如果票数大于0就可以正常卖票 if(ticketNums > 0){ System.out.println(Thread.currentThread().getName() + "买到了"+ticketNums--+"张票"+"剩余:"+ticketNums+"张票"); } } } //synchronized本质就是队列,锁(排队) //修饰代码块对象为类 public void sale(){ //默认对象是this synchronized (Ticket.class){ //如果票数大于0就可以正常卖票 if(ticketNums > 0){ System.out.println(Thread.currentThread().getName() + "买到了"+ticketNums--+"张票"+"剩余:"+ticketNums+"张票"); } } } }
再次运行发现结果是我们想要的,CPU调度井井有序,像是被排队一样
线程A买到了10张票剩余:9张票
线程A买到了9张票剩余:8张票
线程A买到了8张票剩余:7张票
线程A买到了7张票剩余:6张票
线程A买到了6张票剩余:5张票
线程A买到了5张票剩余:4张票
线程A买到了4张票剩余:3张票
线程A买到了3张票剩余:2张票
线程A买到了2张票剩余:1张票
线程A买到了1张票剩余:0张票
lock锁
加锁:lock() 解锁:unLock();
lock锁属于JUC下的接口 它的实现类有ReentrantLock(可重入锁,最常用的),ReadLock(读锁),WriterLock(写锁)
观察ReentrantLock源码可以发现 它有一个构造方法(如果传参根据传的参数决定公平或者不公平锁,不传参的话默认是不公平锁) 可以传一个boolean值,如果为true的话是一个公平锁,如果为false的话是一个非公平锁
那么什么是公平锁和非公平锁呢?
- 公平锁(Fairsync )
十分公平,先来后到,根据线程安排调度
- 非公平锁(NonFairsync)
十分不公平,.可以插队(默认是非公平锁 ),根据CPU调度
其实就是锁的执行调度不同,比如有一个3h的线程有一个3s多线程,如果3h排在了3s之前,就会很不公平,所以java默认是不公平锁,synchronized也是如此
注意:lock一般结合try catch finally代码块使用 业务代码写在try中,解锁(unLock())写在finally中
synchronized是全自动的,lock是手动的
使用lock三部曲
- Lock lock=new ReentrantLock(); 可重入锁ReentrantLock类
- 加锁 lock.lock()
- 解锁 lock.unLock()
package com.wyh.demo; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @program: JUC * @description: 卖票 使用lock锁使得线程变得安全 * @author: 魏一鹤 * @createDate: 2022-02-11 22:31 **/ //真正的多线程开发,公司中的开发,降低耦合性 //线程就是一个单独的资源类,没有任何附属的操作 //1 属性 2 方法 public class saleTicketDemo02 { public static void main(String[] args){ //资源类 Ticket2 ticket=new Ticket2(); //多线程操作买票 并发:多个线程操作同一个资源类,把资源类丢入线程就可以了 //FunctionalInterface 函数式接口 jdk1.8 lambda表达式 (参数)->{代码} //for循环只有一句的时候 可以简化"{}" new Thread(()->{ for (int i = 0; i < 10; i++) ticket.sale(); },"线程A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) ticket.sale(); },"线程B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) ticket.sale(); },"线程C").start(); } } //资源类 OOP编程 使用lock锁 class Ticket2 { //属性 方法 //票数 private int ticketNums=10; //lock是一个接口 创建它的实现类 可重入锁ReentrantLock实现类 //synchronized是全自动的,lock是手动的 //lock三部曲 //1 Lock lock=new ReentrantLock(); 可重入锁ReentrantLock类 //2 加锁 lock.lock() //3 解锁 lock.unLock() Lock lock=new ReentrantLock(); //卖票方法 public void sale() { //注意 lock一般结合try catch finally代码块使用 //加锁 lock.lock(); try { //业务代码写在try中 //如果票数大于0就可以正常卖票 if (ticketNums > 0) { System.out.println(Thread.currentThread().getName() + "买到了" + ticketNums-- + "张票" + "剩余:" + ticketNums + "张票"); } } catch (Exception e) { e.printStackTrace(); } finally { //解锁一定在finally中 //解锁 lock.unlock(); } } }
运行代码通过console控制台发现,效果和synchronized是一样的,不同的是synronzied是全自动的,lock锁是手动的