1.线程创建的三种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
1.1 创建线程方式① 继承Thread类
- 继承Thread类
- 重写run方法
- 创建线程对象
- 线程对象调用star()方法执行
/** 创建线程方式1 实现多线程图片下载 需要导入 commons-io-2.11.0.jar */
public class TestThread2 extends Thread {
@Override
public void run() {
WebDownload webDownload = new WebDownload();
webDownload.downLoader(url,fileName);
System.out.println("下载图片:"+fileName);
}
private String url;
private String fileName;
public TestThread2(String url,String fileName){
this.url = url;
this.fileName = fileName;
}
public static void main(String[] args) {
TestThread2 testThread1 = new TestThread2("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-004.jpg","图片1.jpg");
TestThread2 testThread2 = new TestThread2("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-005.jpg","图片2.jpg");
TestThread2 testThread3 = new TestThread2("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-006.jpg","图片3.jpg");
//线程对象调用star()方法执行
testThread1.start();
testThread2.start();
testThread3.start();
}
}
class WebDownload{
public void downLoader(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url),new File(fileName));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoad方法出现问题!");
}
}
}
1.2 创建线程方式② Runnable接口
- 实现Runnable接口
- 重写run()方法
- 创建Thread对象
- 将线程对象传递给Thread对象代理执行
- 由Thread对象调用start()执行
/** 创建线程方式二 实现Runnable接口 龟兔赛跑 */
public class Race implements Runnable {
private static String Winner;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"---->走了第"+i+"步");
boolean flag = GameOver(i);
if (flag){return;}
}
}
public static void main(String[] args) {
Race race = new Race();
//将线程对象传递给Thread对象代理执行
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
private boolean GameOver(int steps){
if (Winner!=null){
return true;
}
else {
if (steps>=100){
Winner = Thread.currentThread().getName();
System.out.println(Winner+"is winner!");
return true;
}
}
return false;
}
}
区别:通过继承Thread类创建线程,受到了单继承的制约。实现Runnable接口更加灵活
1.3 创建线程方式③ 实现Callable接口
- 实现Callable接口
- 重写call()方法
- 创建线程对象
- 创建执行服务(线程池)
- 提交执行(返回Future对象)
- 关闭服务
/** 线程创建方式3:实现callable接口 */
public class ThreadCallable implements Callable<Boolean> {
private String url;
private String fileName;
public ThreadCallable(String url,String fileName){
this.url = url;
this.fileName = fileName;
}
@Override
public Boolean call(){
WebDownload webDownload = new WebDownload();
webDownload.downLoader(url,fileName);
System.out.println("下载图片:"+fileName);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadCallable t1 = new ThreadCallable("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-004.jpg","图片1.jpg");
ThreadCallable t2 = new ThreadCallable("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-005.jpg","图片2.jpg");
ThreadCallable t3 = new ThreadCallable("https://img-pre.ivsky.com/img/tupian/pre/202103/07/xuejing-006.jpg","图片3.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r1.get();
boolean rs3 = r1.get();
//关闭服务
ser.shutdownNow();
}
}
class WebDownload{
public void downLoader(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url),new File(fileName));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoad方法出现问题!");
}
}
}
1.31 实现Runnable接口 和 Callable接口的区别:
Runnable 接口不会返回结果或抛出检查异常,但是Callable 接口可以。所以,如果任务不需要返回结果或抛出异常推荐使用 Runnable 接口
1.32 执行 execute()方法和 submit()方法的区别?
execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;submit()方法用于提交需要返回值的任务。线程池会返回一个Future类型的对象,通过这个Future对象可以判断任务是否执行成功,并且可以通过Future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
1.4 wait()和sleep()的区别?
- 所属的类不同:wait —> Object sleep —>Thread
- 释放锁: wait —> 释放锁(最好的等待是说放开) sleep —> 不释放锁(抱着锁睡)
- 使用的范围不同:wait —> 只能在同步代码块内 sleep —> 哪儿都能用
- 是否需要捕获异常: wait —> 不需要捕获异常 sleep —> 需要捕获异常
1.5 Lock锁和synchronied锁有什么区别?
- synchronied是Java内置关键字,Lock是Java类
- synchronied不能获取锁的状态,Lock可以获取锁的状态
- synchronied会自动释放锁,Lock需要手动释放锁,如果不释放—>死锁
- synchronied可重入锁,不可中断,非公平,Lock可重入锁,可判断所哦哦,非公平(可设置)
- synchronied 线程1(获取,阻塞)—->线程2(等待,傻傻的等),Lock锁不会一直等—>Lock.try();
- synchronied适合锁少量的同步代码,Lock适合锁大量的同步代码
1.6 锁的虚假唤醒
等待没有写到while里面,例如wait()语句写在了if语句里面,这样假设存在,A、B、C、D线程就会导致虚假唤醒。避免虚假唤醒—>使用while代替if;因为当其他线程调用notifyAll的时候,由于在while语句内,所以它还会在进行判断是否满足条件。而如果使用的if则当其他线程调用notifyAll的时候其他线程会继续往下执行,因为if只判断一次
// synchronized + wait() + notifyAll();
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
}
}
class Data{
//判断等待--》业务--》通知
private int number = 0;
public synchronized void increment() throws InterruptedException {
//等待
while (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//等待
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知
this.notifyAll();
}
}
JUC版 生产者和消费者问题
// JUC ---- Lock + await() + signalAll();
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"D").start();
}
}
class Data2{
//判断等待--》业务--》通知
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() {
lock.lock();
try {
//等待
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
//等待
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

解决—>指定唤醒:
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{
//判断等待--》业务--》通知
private int number = 1;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
public void printA(){
lock.lock();
try {
while (number!=1){
//该方法拥有的监视器为condition
condition.await();
}
System.out.println(Thread.currentThread().getName()+"正在执行-----》AAAAAA");
number = 2;
//指定唤醒监视器
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number!=2){
//该方法拥有的监视器为condition1
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"正在执行-----》BBBBBB");
number = 3;
//指定唤醒监视器
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number!=3){
//该方法拥有的监视器为condition2
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"正在执行-----》CCCCCC");
number = 1;
//指定唤醒监视器
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
1.7 8锁现象
- new/this 锁的是用的对象
- Static 锁的是Class模板
顺序执行:谁先调用先执行谁
随机执行:没有规律,与计算机硬件资源有关,哪个线程先得到资源就先执行,各个线程之间互不干扰。
对此锁的问题,我们先判断是不是同一把锁,如果是同一把锁,那么就是谁先拿到锁,先执行哪个直到它结束—>顺序执行。如果不是同一把锁两者之间不存在竞争—>随机执行,输出的结果就看谁没有延迟,谁的结果先显示,如果都没有延迟则运行结果是随机的;不加关键字的普通方法跟锁没有关系。
1.8 并发编程下ArrayList和HashSet是不安全的,如何解决呢?
使用Collections.synchronizedSet()
List<Object> list = Collections.synchronizedSet(new ArrayList<>());//ArrayList Set<Object> set = Collections.synchronizedSet(new HashSet<>());//HashSet
2.使用JUC包下提供的类
List list = new CopyOnWriteArrayList();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
Set<Object> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
1.82 Callable —> FutureTask —> Thread
public class G {
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable Thread = new TestCallable();
FutureTask futureTask = new FutureTask(Thread);
//两个线程只会输出一个Call()---》缓存提高效率
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();
//因为他需要获取返回的结果(如果遇到耗时的操作)--->可能会发生阻塞-->所以放在最后面
System.out.println(futureTask.get());
}
}
class TestCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Call()");
return "Hello World!";
}
}
1.9 CountDownLatch(减法计数器)
public class DownLatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" Already Go Out!");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
//等待计数器归零后唤醒向下执行
countDownLatch.await();
System.out.println("大门锁死!");
}
}
2.0 CyclicBarrier(加法计数器)
public class cbarry {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙!!!");
});
for (int i = 1; i <=7; i++) {
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收集了第"+temp+"颗龙珠");
try {
//等待执行7次后唤醒---->执行上面的代码--->召唤神龙
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
2.1 Semaphore —- 信号量
public class semaphore {
//抢车位例子
public static void main(String[] args) {
//类似抢车位的位置
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
//阻塞--->停车位被占据
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"获得了车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//release()释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
2.2 ReadWriteLock — 读写锁
写锁是独占锁:有且仅有一个线程可以获得写模式下的锁,即当一个线程获取了写模式的锁,其他的线程不能对其以写模式或者读模式在加锁;
读锁是共享锁:当多个线程同时以读模式加锁的时候,都将被允许。即允许多个线程同时进行读操作,但是如果想以写模式进行加锁,那么将会在所有读模式锁都释放的情况下被允许。但是在系统当中一般会阻塞接下来的读操作,让写操作进行,为了避免写操作阻塞时间过长
public class readwrightLock {
public static void main(String[] args) {
TestRwLock MyTest = new TestRwLock();
//数据存储
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(()->{
MyTest.put(temp+ "",temp);
},String.valueOf(i)).start();
}
//数据读取
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(()->{
MyTest.get(String.valueOf(temp));
},String.valueOf(i)).start();
}
}
}
class TestRwLock{
Map<String,Object> map = new HashMap<>();
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public synchronized void put(String key,Object value){
//加写锁
readWriteLock.writeLock().lock();
try {
map.put(key,value);
System.out.println(key+"正在写入。。。");
System.out.println(key+"写入成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
readWriteLock.writeLock().unlock();
}
}
public void get(String key){
//加读锁
readWriteLock.readLock().lock();
try {
Object o = map.get(key);
System.out.println("读取 "+key+" 的Value值为:"+o);
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
readWriteLock.readLock().unlock();
}
}
}
2.3 阻塞队列 BlockingQueue

四组API
| 方式 | 抛出异常 | 有返回值不抛出异常 | 阻塞 等待 | 超时 等待 |
|---|---|---|---|---|
| 添加 | add() | offer() | put() | offer(等待时间,TimeUtil.Second) |
| 移除 | remove() | poll() | take() | poll(等待时间,TimeUtil.Second) |
| 检查队首元素 | element() | peek() |
//声明可以容量3个元素的阻塞队列
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//================add()与remove()===============
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//抛出异常---超出容量
//System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//抛出异常---队列为空无法继续移除
//System.out.println(blockingQueue.remove());
//输出队首元素---a element()无元素抛出异常 peek()无元素返回 null
System.out.println(blockingQueue.element());
System.out.println(blockingQueue.peek());
//================offer()poll()===============
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//不会抛出异常会返回false----成功返回true
System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//不会抛出异常失败返回null----成功取出相应的值
System.out.println(blockingQueue.poll());
//================put()take()===============
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//操作阻塞,程序一直等待直到可以把"d"加入队列才往下执行
blockingQueue.put("d");
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
//操作阻塞,程序一直等待直到可以拿出元素才往下执行
blockingQueue.take();
//================offer(,)和poll(,)===============
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
//当2s后依然无法加入队列则停止加入队列,往下执行
blockingQueue.offer("d",2,TimeUnit.SECONDS);
blockingQueue.poll();
blockingQueue.poll();
blockingQueue.poll();
//当2s后依然无法获取元素则停止获取,往下执行
blockingQueue.poll(2,TimeUnit.SECONDS);
2.5 同步队列 SynchronizedQueue
public class SynchrniedQueue {
public static void main(String[] args) {
SynchronousQueue<String> syq = new SynchronousQueue<>();
new Thread(()->{
try {
syq.put("a");
System.out.println(Thread.currentThread().getName()+"Put===》a");
syq.put("b");
System.out.println(Thread.currentThread().getName()+"Put===》b");
syq.put("c");
System.out.println(Thread.currentThread().getName()+"Put===》c");
syq.put("d");
System.out.println(Thread.currentThread().getName()+"Put===》d");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"线程1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
syq.take();
System.out.println(Thread.currentThread().getName()+"Take===》a");
TimeUnit.SECONDS.sleep(3);
syq.take();
System.out.println(Thread.currentThread().getName()+"Take===》b");
TimeUnit.SECONDS.sleep(3);
syq.take();
System.out.println(Thread.currentThread().getName()+"Take===》c");
TimeUnit.SECONDS.sleep(3);
syq.take();
System.out.println(Thread.currentThread().getName()+"Take===》d");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"线程2").start();
}
}
只能放一个取一个,如果将线程2注释掉,会出现线程1被阻塞的状态。放一个a进去,另外一个线程取出a,在放b进去,另外一个线程取出b。。。。以此类推。同步队列—只能放一个元素进去
2.6 线程池创建线程
public class DemonThreadPool {
public static void main(String[] args) {
//创建单个线程
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//创建一个固定的线程池大小
//ExecutorService threadPool = Executors.newFixedThreadPool(5);
//可伸缩,遇强则强,遇弱则弱
//ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"====>OK baby!");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool.shutdown();
}
}
}
2.7 ThreadExcutor创建线程
public class ThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//最大承载量 maxMumPoolSize+队列长度 = 5+3
try {
for (int i = 0; i < 7; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"====>OK baby!");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}


//默认拒绝策略---超出最大处理数量--->不处理并抛出异常
new ThreadPoolExecutor.AbortPolicy()
//超出最大处理数量(队列满了)--->交由其原线程执行(main)
new ThreadPoolExecutor.CallerRunsPolicy()
//队列满了,不会抛出异常,丢掉任务
new ThreadPoolExecutor.DiscardPolicy()
// 队列满了,也不会抛出异常,但是会尝试丢弃最旧的任务把新任务添加进去
new ThreadPoolExecutor.DiscardOldestPolicy()
2.8 四种函数型接口
/** Consumer消费型接口 */
//Consumer<String> consumer = new Consumer() {
// @Override
// public void accept(Object o) {
// System.out.println(o);
// }
//};
//
Consumer<String> consumer = (str)->{
System.out.println(str);
};
consumer.accept("hello world");
/** supplier供给型接口 */
//Supplier supplier = new Supplier() {
// @Override
// public Object get() {
// System.out.println("你已经进入Get()");
// return 1024;
// }
//};
Supplier supplier = ()->{
System.out.println("你已经进入Get()");
return 1024;
};
supplier.get();
/** Function 函数型接口 */
//Function<String, String > function = new Function<String, String>() {
// @Override
// public String apply(String o) {
// return o;
// }
//};
Function<String, String > function = (str)->{
return str;
};
System.out.println(function.apply("Fengfeng"));
/** Predicate 断定型接口---返回值只有False或者True */
//Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String s) {
// Boolean flag = false;
// if (!s.isEmpty()){flag=true;}
// return flag;
// }
//};
Predicate<String> predicate = (s)->{
Boolean flag = false;
if (!s.isEmpty()){flag=true;}
return flag;
};
System.out.println(predicate.test("Hello world"));
2.9 JMM

关于JMM同步的一些约定:
- 线程解锁前,必须把共享变量立即刷回主存
- 线程加锁前,必须读取主存的最新值到工作内存
- 加锁和解锁是同一把锁
3.0 什么是指令重排?
就是你写的程序,计算并不是按照你写的那样去执行
源代码—>编译器优化重排—>指令并行也会重排—>内存系统也会重排—>执行
3.1 可重入锁
某个线程获得了锁之后可以再次获取锁(套娃)而不会产生死锁,通俗点讲就是一扇门里面还有一扇门,你拿到第一扇门的钥匙后会自动获取第二扇门的钥匙。
3.11 ABA问题—->解决—>原子引用
例子:桌子上又一个装满水的杯子,你路过之后把杯中的水喝完,又重新倒满了杯中的水,这时候杯子的主人回来,他看到杯子的水是满的,以为杯子的水没人动过
3.2 自旋锁
public class SpinLock {
//原子类
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void mylock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"进入MyLock请等待!");
while (!atomicReference.compareAndSet(null,thread)){}
}
public void myunlock(){
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"解锁成功!");
}
}
public class TestSpinLock {
public static void main(String[] args) throws Exception {
SpinLock spinLock = new SpinLock();
new Thread(()->{
spinLock.mylock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinLock.myunlock();
}
},"线程A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinLock.mylock();
try {
} catch (Exception e) {
e.printStackTrace();
} finally {
spinLock.myunlock();
}
},"线程B").start();
}
}

线程A获取了锁,线程B无法获取锁只能被迫进入自旋状态卡在了这行代码上,
while (!atomicReference.compareAndSet(null,thread)){}
直到线程A解锁后将atomicReference的值重置为null后,线程B才可以获得锁
3.3 排查死锁
- jps -l

- jstack+进程号 —->获取当前进程堆栈信息—–>查看是否产生死锁问题