代码
package com.auth.Sercurity.TreadDemo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/**
* @author hujian
* @version 1.0
* @description: 线程模式售票
* @date 2022/2/22 15:29
*/
public class TicketDemo {
protected static List<Integer> list=new ArrayList<>();
static class TicketThread extends Thread{
private static Integer tickets=100;
private static final ReentrantLock lock=new ReentrantLock();
private Object objectLock=new Object();
@Override
public void run(){
while (tickets>0){
selaTicketLock();
// 或者选择Synchornized方法
// selaTicketSychronyzed();
}
System.out.println("==========");
System.out.println(Thread.currentThread().getName()+"售票结束");
}
private void selaTicketLock() {
try {
// 代码加锁
lock.lock();
if (tickets>0){
System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
list.add(tickets);
tickets--;
}
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
// 必须手动解锁
lock.unlock();
}
}
/**
* 如果是实例方法,那么默认的同步监听对象就是this,
* 如果是静态方法,默认的同步监听对象是类的字节码对象;
* 1.实例方法 private synchronized void sellTicketSyn(),默认的同步监听对象就是this,只能锁住同一个对象,在当前使用会出现重复售票;
* 2.静态方法 private static synchronized void sellTicketSyn(),同步监听对象是类的字节码对象,相当于全局锁;
*/
private void selaTicketSychronyzed() {
/**
* 1.synchronized锁住的是括号里得对象,而不是代码
* 2.常用的锁对象(this,字节码)
* a.当synchronized (TicketThread.class),程序正常执行,只有一份字节码,谁拥有字节码就有执行权
* b.当chnchronized(objectLock)或chnchronized(this),this代表当前对象,启动线程的时候看的实例了几次 TicketThread 类,如果
* TicketThread ticketThread = new TicketThread();
* Thread thread1=new Thread(ticketThread,"窗口1");
* Thread thread2=new Thread(ticketThread,"窗口2");
* 那么只会有一个objectLock对象或者TicketThread对象。如果
* Thread thread1= new TicketThread();
* Thread thread2= new TicketThread();
* 那么就会多个objectLock对象或者TicketThread对象,也会重复卖票
* c.synchronized(tickets),因为数量是变化的,所以当存在数量不相等时会同时进行。
*
*/
synchronized (this){
if (tickets>0){
System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
list.add(tickets);
tickets--;
}
}
}
}
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
Thread thread1=new Thread(ticketThread,"窗口1");
Thread thread2=new Thread(ticketThread,"窗口2");
Thread thread3=new Thread(ticketThread,"窗口3");
// Thread thread1= new TicketThread();
// Thread thread2= new TicketThread();
// Thread thread3= new TicketThread();
// thread1.setName("窗口1");
// thread2.setName("窗口2");
// thread3.setName("窗口3");
System.out.println("开始售票");
System.out.println("=================");
thread1.start();
thread2.start();
thread3.start();
// 提取出重复的票数,供自己参考
List<Integer> result=new ArrayList<>();
Map<Integer, Long> collect = list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
collect.forEach((a,b)->{
if (b>1){
result.add(a);
}
});
System.out.println(result.toString());
System.out.println(list.toString());
}
}
特性
1.锁的对象
- synchorized锁的是对象,锁是保存在对象头里面的,根据对象头的数据来标识是否有线程获得锁和争抢锁。
- ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的状态。
2.底层实现
- synchronized是JVM层面的锁,是Java的关键字,通过monitor的对象完成。对象只有在同步代码块或同步方法中才能调用wait/notify方法。synchronized的实现涉及锁的升级,具体为无锁,偏向锁,自旋锁,向OS申请重量级锁。
- ReentrantLock实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
3.是否可手动释放
- synchroized不需要用户自己去释放锁,Synchronized锁的代码执行完后系统会自动让线程释放对锁的占用;
- ReentrantLocak则需要用户手动加锁和释放锁,不手动使用unlock()可能会导致死锁现象。
4.是否可绑定条件
synchronized不能绑定,ReentrantLock通过绑定Condition结合await()/singal()方法实现线程的精准唤醒,而不是像synchronized通过Object类wait()/notify()/notifyAll()那样要么随即唤醒要么全部唤醒。
package com.auth.Sercurity.TreadDemo;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author hujian
* @version 1.0
* @description: ReentrantLock绑定条件
* 依次打印 A/B/C
* @date 2022/2/23 14:38
*/
public class ReentrantLockCondition {
int number=1;
private ReentrantLock lock=new ReentrantLock();
private Condition condition1=lock.newCondition();
private Condition condition2=lock.newCondition();
private Condition condition3=lock.newCondition();
void print1(){
lock.lock();
try {
while (number!=1){
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"---"+number);
number=2;
// 唤醒
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
void print2(){
lock.lock();
try {
while (number!=2){
// 等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"---"+number);
number=3;
// 唤醒
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
void print3(){
lock.lock();
try {
while (number!=3){
// 等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"---"+number);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
//
public static void main(String[] args) {
ReentrantLockCondition reentrantLockCondition=new ReentrantLockCondition();
new Thread(()-> reentrantLockCondition.print1(),"A:").start();
new Thread(()->reentrantLockCondition.print2(),"B:").start();
new Thread(()->reentrantLockCondition.print3(),"C:").start();
}
}
5.是否公平
synchronized为非公平锁 ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。
private ReentrantLock lock=new ReentrantLock(true);
private ReentrantLock lock=new ReentrantLock(false);
6是否可以中断
- synchronized是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成;
- ReentrantLock则可以中断,可通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断。
try {
// 代码加锁
lock.lock();
lock.tryLock(2, TimeUnit.SECONDS);
lock.lockInterruptibly();
if (tickets>0){
System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
list.add(tickets);
tickets--;
}
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
// 必须手动解锁
lock.unlock();
}