JavaJUC学习篇 什么是JUC java.util 工具包、包、分类
线程和进程 进程:一个程序,比如qq,酷我音乐啥的一个进程往往包含一个多个线程。
线程:java默认两个线程一个main,一个GC
java线程实现的方式:
继承Thread,实现Runnable接口,实现callable
并行和并发 并行:多线程操作处理同一个资源,或者说多个人完成同一件事情
并发:为了达到某件事,每个人负责不同的模块然后完成事情。
线程的状态: 创建 —》 就绪 —》 运行 —》阻塞 —>死亡
(我们通常分为五种,但是有些情况下分为7种,加的两种为等待和超时等待)
wait和sleep的区别 首先:
wait来自Object
sleep来自Thread
其次:
wait会释放锁
sleep不会
使用范围不一样:
wait必须放到同步块中
sleep没有要求
Lock和Synchonized 传统的synchonized锁
synchonized(){
}
括号里边一般两种
1对象
2类名.class
还有 private synchonized void name(){}
Lock接口
ReentrantLock: 可重用锁
readLock: 读锁
writeLock:写锁
Synchronized 和 Lock 区别
1、Synchronized 内置的Java关键字, Lock 是一个Java类
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
去;
5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
自己设置);
6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
生产者消费者问题
生产者和消费者问题Synchronized版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.xh.juc.day02;public class A { public static void main (String[] args) { Date date = new Date (); new Thread (()->{ for (int i = 0 ; i < 10 ; i++) { try { date.increment(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"A" ).start(); new Thread (()->{ for (int i = 0 ; i < 10 ; i++) { try { date.decrement(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"B" ).start(); } } class Date { private int number = 0 ; public synchronized void increment () throws InterruptedException { if (number != 0 ){ this .wait(); } number ++ ; System.out.println(Thread.currentThread().getName() + "=>" +number); this .notifyAll(); } public synchronized void decrement () throws InterruptedException { if (number == 0 ){ this .wait(); } number --; System.out.println(Thread.currentThread().getName() + "=>" +number); this .notifyAll(); } }
生产者消费者问题:两个还好,但是到了三个或者四个就会产生问题
面试常问: 单例模式,排序算法,生产者消费者问题,死锁
问题存在:A,B,C,D四个线程的情况下出现问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class B { public static void main (String[] args) { Date date = new Date (); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { date.increment(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"A" ).start(); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { date.increment(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"B" ).start(); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { date.decrement(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"C" ).start(); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { date.decrement(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"D" ).start(); } }
A=>1 C=>0 B=>1 A=>2 B=>3 C=>2 C=>1 C=>0 B=>1 A=>2 B=>3 C=>2 B=>3 A=>4 D=>3 D=>2 D=>1 D=>0 A=>1 D=>0
以上原因是由于线程产生了虚假唤醒,导致出现问题。
==解决办法,我们将if改成while判断==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Date1 { 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(); } }
Lock解决生产者和消费者问题JUC版本的
基本实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 public class C { public static void main (String[] args) { Data data = new Data (); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { data.increment(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"A" ).start(); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { data.increment(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"B" ).start(); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { data.decrement(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"C" ).start(); new Thread (()->{ for (int i = 0 ; i < 5 ; i++) { try { data.decrement(); } catch (InterruptedException e) { throw new RuntimeException (e); } } },"D" ).start(); } } class Data { private int number = 0 ; Lock lock = new ReentrantLock (); Condition condition = lock.newCondition(); public void increment () throws InterruptedException { try { lock.lock(); while (number != 0 ){ condition.await(); } number ++; System.out.println(Thread.currentThread().getName()+"=>" + number); condition.signalAll(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public void decrement () throws InterruptedException { try { lock.lock(); while (number == 0 ) { condition.await(); } number--; System.out.println(Thread.currentThread().getName() +"=>" + number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
任何一个新的技术绝对不是仅仅覆盖了一起的技术,一定会在原来的技术上进行相应的拓展
Condtion实现精准唤醒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public class D { public static void main (String[] args) { Data2 data2 = new Data2 (); new Thread (()->{ for (int i = 0 ; i < 10 ; i++) { data2.printA(); } },"A" ).start();; new Thread (()->{ for (int i = 0 ; i < 10 ; i++) { data2.printB(); } },"B" ).start(); new Thread (()->{ for (int i = 0 ; i < 10 ; i++) { data2.printC(); } },"C" ).start();; } } class Data2 { private int number = 1 ; private Lock lock = new ReentrantLock (); private Condition condition = lock.newCondition(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); public void printA () { lock.lock(); try { while (number != 1 ){ condition.await(); } System.out.println(Thread.currentThread().getName()+ "==>AA" ); number = 2 ; condition1.signal(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public void printB () { lock.lock(); try { while ( number != 2 ){ condition1.await(); } System.out.println(Thread.currentThread().getName()+ "==>BB" ); number = 3 ; condition2.signal(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public void printC () { lock.lock(); try { while (number != 3 ){ condition2.await(); } System.out.println(Thread.currentThread().getName() + "==>CC" ); number =1 ; condition.signal(); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } }
什么是锁,如何判断锁对象(8种锁): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Test1 { public static void main (String[] args) throws InterruptedException { Phone phone = new Phone (); new Thread (()->{ try { phone.sendSms(); } catch (InterruptedException e) { throw new RuntimeException (e); } },"A" ).start(); TimeUnit.SECONDS.sleep(1 ); new Thread (()->{ phone.sendTel(); },"B" ).start(); } } class Phone { public synchronized void sendSms () throws InterruptedException { TimeUnit.SECONDS.sleep(2 ); System.out.println("发短信" ); } public synchronized void sendTel () { System.out.println("打电话" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Test2 { public static void main (String[] args) throws InterruptedException { Phone2 phone2 = new Phone2 (); Phone phone = new Phone (); new Thread (()->{ try { phone.sendSms(); } catch (InterruptedException e) { throw new RuntimeException (e); } },"A" ).start(); TimeUnit.SECONDS.sleep(1 ); new Thread (()->{ phone2.sendTel(); },"B" ).start(); } } class Phone2 { public synchronized void sendSms () throws InterruptedException { TimeUnit.SECONDS.sleep(2 ); System.out.println("发短信" ); } public synchronized void sendTel () { System.out.println("打电话" ); } public void hello () { System.out.println("说hello" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Test3 { public static void main (String[] args) throws InterruptedException { Phone3 phone3 = new Phone3 (); Phone phone = new Phone (); new Thread (()->{ try { phone.sendSms(); } catch (InterruptedException e) { throw new RuntimeException (e); } },"A" ).start(); TimeUnit.SECONDS.sleep(1 ); new Thread (()->{ phone3.sendTel(); },"B" ).start(); } } class Phone3 { public static synchronized void sendSms () throws InterruptedException { TimeUnit.SECONDS.sleep(2 ); System.out.println("发短信" ); } public static synchronized void sendTel () { System.out.println("打电话" ); } public void hello () { System.out.println("说hello" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import java.util.concurrent.TimeUnit;public class Test4 { public static void main (String[] args) { Phone4 phone4 = new Phone4 (); Phone2 phone2 = new Phone2 (); new Thread (()->{ try { phone4.sendSms(); } catch (InterruptedException e) { throw new RuntimeException (e); } }).start(); new Thread (()->{ phone2.sendTel(); }).start(); } } class Phone4 { public static synchronized void sendSms () throws InterruptedException { TimeUnit.SECONDS.sleep(2 ); System.out.println("发短信" ); } public synchronized void call () { System.out.println("打电话" ); } }
new this 具体的一个手机
static Class 唯一的一个模板
集合类是不安全的
List
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class ListTest { public static void main (String[] args) { List<String> list = new CopyOnWriteArrayList <>(); for (int i = 1 ; i <= 10 ; i++) { new Thread (()->{ list.add(UUID.randomUUID().toString().substring(0 ,5 )); System.out.println(list); },String.valueOf(i)).start(); } } }
Set的不安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SetTest { public static void main (String[] args) { Set<String > 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(); } } }
HashSet的底层就是HashMap
1 2 3 4 5 6 7 8 9 public HashSet () { map = new HashMap <>(); } public boolean add (E e) { return map.put(e,PRESENT) == null ; } private static final object PRESENT = new Object ();
HashMap
回顾hashMap的 简单用法加载因子为0.75
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class HashMapTest { public static void main (String[] args) { Map<String,String> map = new ConcurrentHashMap <>(); for (int i = 0 ; i < 10 ; i++) { new Thread (()->{ map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0 ,5 )); System.out.println(map); },String.valueOf(i)).start(); } } }
Callable重点讲解
有返回值
可以抛出异常
方法不同,run()/call()
代码测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class CallabelTest { public static void main (String[] args) throws ExecutionException, InterruptedException { new Thread ().start(); MyThread1 thread = new MyThread1 (); FutureTask futureTask = new FutureTask (thread); new Thread (futureTask,"A" ).start(); new Thread (futureTask,"B" ).start(); Integer o = (Integer)futureTask.get(); System.out.println(o); } } class MyThread implements Runnable { @Override public void run () { } } class MyThread1 implements Callable <Integer>{ @Override public Integer call () throws Exception { System.out.println("Call()" ); return 123 ; } }
细节:
存在缓存
结果可能需要等待,会阻塞
常用的辅助类(必须掌握) CountDownLatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CountDownLatchDemo { public static void main (String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch (6 ); for (int i = 0 ; i < 6 ; i++) { new Thread ( ()->{ System.out.println(Thread.currentThread().getName() + " \t GO OUT" ); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await(); System.out.println("关门" ); } }
减法计数器:
原理是数量减一:
1 countDownLatch.countDown();
每次有线程调用countDown()数量1,假设计数器变为0,countDownLatch.await就会被唤醒,继续执行!
CycliBarrier: 加法计数器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class CycliBarrierDemo { public static void main (String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier (6 ,()->{ System.out.println("事件完成" ); }); for (int i = 1 ; i < 7 ; i++) { final int temp = i; new Thread ( ()->{ System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠" ); try { cyclicBarrier.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } catch (BrokenBarrierException e) { throw new RuntimeException (e); } }).start(); } } }
Semaphore : 信号量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SemaphoreDemo { public static void main (String[] args) { Semaphore semaphore = new Semaphore (6 ); 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) { throw new RuntimeException (e); } finally { semaphore.release(); } },String.valueOf(i)).start(); } } }
原理:
semaphore.acquire();获得,假如已经满了,等待,等待被释放
semaphore.release();释放,会将当前的信号量释放 +1 ,然后唤醒等待的线程。
读写锁 ReadWriteLock:读可以被多个线程读,但写的时候只能是一个线程写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public class ReadWriteLockDemo { public static void main (String[] args) { MyCacheLock myCache = new MyCacheLock (); for (int i = 0 ; i < 5 ; i++) { final int temp = i; new Thread (()->{ myCache.put(temp + "" ,temp +"" ); },String.valueOf(i)).start(); } for (int i = 0 ; i < 5 ; i++) { final int temp = i; new Thread (()->{ myCache.get(temp + "" ); },String.valueOf(i)).start(); } } } class MyCache { private volatile Map<String,Object> map = new HashMap <>(); public void put (String key , Object obj) { System.out.println(Thread.currentThread().getName()+ "写入" + key); map.put(key,obj); System.out.println(Thread.currentThread().getName()+"写入成功" ); } public void get (String key) { System.out.println(Thread.currentThread().getName()+ "读取" + key); Object obj = map.get(key); System.out.println(Thread.currentThread().getName() + "读取成功" ); } } class MyCacheLock { private volatile Map<String,Object> map = new HashMap <>(); private ReadWriteLock lock = new ReentrantReadWriteLock (); public void put (String key , Object obj) { lock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+ "写入" + key); map.put(key,obj); System.out.println(Thread.currentThread().getName()+"写入成功" ); }catch (Exception e){ e.printStackTrace(); }finally { lock.writeLock().unlock(); } } public void get (String key) { lock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+ "读取" + key); Object obj = map.get(key); System.out.println(Thread.currentThread().getName() + "读取成功" ); }catch (Exception e){ e.printStackTrace(); }finally { lock.readLock().unlock(); } } }
阻塞队列 队列(FIFO)
写入的时候:如果队列是满的,那么久必须阻塞等待。
取出来的时候:如果队列是空的,那么必须阻塞等待生产。
BlockingQueue
在线程下,我们使用的时候需要注意阻塞队列是多线程下并发处理,线程池处理。
学会使用队列
添加,移除
四组API (熟练掌握并使用 )
方式
抛出异常
不会抛出异常
阻塞等待
超时等待
添加
add
offer
put
offer(,,)
移除
remove
poll
take
poll(,)
判断队列首位
element
peek
—
—
抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class BlockingQueueDemo { public static void main (String[] args) { test1(); } public static void test1 () { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue <>(3 ); System.out.println(blockingQueue.add("a" )); System.out.println(blockingQueue.add("b" )); System.out.println(blockingQueue.add("c" )); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); } }
不会抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void test2 () { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue <>(3 ); System.out.println(blockingQueue.offer("a" )); System.out.println(blockingQueue.offer("b" )); System.out.println(blockingQueue.offer("c" )); System.out.println(blockingQueue.offer("d" )); System.out.println(blockingQueue.peek()); System.out.println("===========================" ); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); }
阻塞 , 等待
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void test3 () throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue <>(3 ); blockingQueue.put("a" ); blockingQueue.put("b" ); blockingQueue.put("c" ); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); System.out.println(blockingQueue.take()); }
超时等待
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void test4 () throws InterruptedException { ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue <>(3 ); blockingQueue.offer("a" ); blockingQueue.offer("b" ); blockingQueue.offer("c" ); blockingQueue.offer("d" ,2 , TimeUnit.SECONDS); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); blockingQueue.poll(2 ,TimeUnit.SECONDS); }
SynchronousQueue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class SynchronousQueueDemo { public static void main (String[] args) { SynchronousQueue synchronousQueue = new SynchronousQueue <>(); new Thread (()->{ try { System.out.println(Thread.currentThread().getName() + "put 1" ); synchronousQueue.put("1" ); System.out.println(Thread.currentThread().getName() + "put 2" ); synchronousQueue.put("2" ); System.out.println(Thread.currentThread().getName() + "put 3" ); synchronousQueue.put("3" ); } catch (InterruptedException e) { throw new RuntimeException (e); } },"T1" ).start(); new Thread (()->{ try { TimeUnit.SECONDS.sleep(3 ); System.out.println(Thread.currentThread().getName() +"=>" + synchronousQueue.take() ); TimeUnit.SECONDS.sleep(3 ); System.out.println(Thread.currentThread().getName() +"=>" + synchronousQueue.take()); TimeUnit.SECONDS.sleep(3 ); System.out.println(Thread.currentThread().getName()+"=>" + synchronousQueue.take() ); } catch (InterruptedException e) { throw new RuntimeException (e); } },"T2" ).start(); } }
线程池(重点)
池化技术
程序的运行,本质:占用系统的资源,优化资源的使用!=>池化技术
线程池,连接池,内存池,对象池。。。 创建,销毁,十分浪费资源
池化技术,事先准备好一些资源,有人要用,就可以来我这里拿,用完之后还给我
线程池的好处
降低资源的消耗
提高响应的速度
方便管理
线程复用,可以控制最大的并发数,管理线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Demo1 { public static void main (String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); try { for (int i = 0 ; i < 10 ; i++) { final int temp = i; threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+ "=>" + "OK" ); }); } } catch (Exception e) { throw new RuntimeException (e); } finally { threadPool.shutdown(); } } }
7大参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public static ExecutorService newFixedThreadPool (int nThreads) { return new ThreadPoolExecutor (nThreads, nThreads, 0L , TimeUnit.MILLISECONDS, new LinkedBlockingQueue <Runnable>()); } public static ExecutorService newCachedThreadPool () { return new ThreadPoolExecutor (0 , Integer.MAX_VALUE, 60L , TimeUnit.SECONDS, new SynchronousQueue <Runnable>()); } public static ExecutorService newSingleThreadExecutor () { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor (1 , 1 , 0L , TimeUnit.MILLISECONDS, new LinkedBlockingQueue <Runnable>())); } public ThreadPoolExecutor ( int corePoolSize, //创建线程创大小 int maximumPoolSize, //最大核心线程的大小 long keepAliveTime, //超时了,没人调用就好释放 TimeUnit unit, //超时单位 BlockingQueue<Runnable> workQueue, //阻塞队列 ThreadFactory threadFactory, //线程工厂,创建线程的,一般不用动 RejectedExecutionHandler handler //拒绝策略) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0 ) throw new IllegalArgumentException (); if (workQueue == null || threadFactory == null || handler == null ) throw new NullPointerException (); this .acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this .corePoolSize = corePoolSize; this .maximumPoolSize = maximumPoolSize; this .workQueue = workQueue; this .keepAliveTime = unit.toNanos(keepAliveTime); this .threadFactory = threadFactory; this .handler = handler; }
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式, 这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class Demo1 { public static void main (String[] args) { ExecutorService threadPool = new ThreadPoolExecutor ( 2 , 5 , 3 , TimeUnit.SECONDS, new LinkedBlockingQueue <>(3 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .DiscardOldestPolicy() ); try { for (int i = 0 ; i < 10 ; i++) { final int temp = i; threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+ "=>" + "OK" ); }); } } catch (Exception e) { throw new RuntimeException (e); } finally { threadPool.shutdown(); } } }
线程池大小如何去设置
CPU密集型和IO密集型(调优)
1 2 3 4 5 6 7 8 System.out.println(Runtime.getRuntime().availableProcessors());
四大函数接口(必需掌握) 新时代的程序员需要掌握的lambda表达式,链式编程,函数式接口,Stream流式计算
函数式接口:只有一个方法的接口
比如:Runnable
简化开发模型,在新版本中大量使用
比如foreach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class FunctionDemo { public static void main (String[] args) { Function function = new Function <String,String>(){ @Override public String apply (String o) { return o; } }; Function function1 = (str)->{ return str;}; System.out.println(function.apply("asd" )); System.out.println(function1.apply("add" )); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class PredicateDemo { public static void main (String[] args) { Predicate predicate = new Predicate <String >(){ @Override public boolean test (String o) { return o.isEmpty(); } }; Predicate<String> predicate1 = (str)->{return str.isEmpty();}; System.out.println(predicate.test("aad" )); System.out.println(predicate1.test("" )); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class ConsumerDemo { public static void main (String[] args) { Consumer consumer = new Consumer <String>() { @Override public void accept (String o) { System.out.println(o); } }; Consumer<String> consumer1 = (str)->{ System.out.println(str); }; consumer.accept("你好" ); consumer1.accept("我喜欢你" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class SupplierDemo { public static void main (String[] args) { Supplier supplier = new Supplier <String>() { @Override public String get () { return "你好" ; } }; Supplier supplier1 = ()->{return "我还是喜欢你" ;}; System.out.println(supplier.get()); System.out.println(supplier1.get()); } }
老程序员,泛型,反射,枚举都需要会
新程序员,lambda表达式,函数式接口,Stream流式计算
什么是流式计算
大数据:存储+计算
集合,MySql本质上就是存储东西
计算都应该交给流来处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Test { public static void main (String[] args) { User u1 = new User (1 ,12 ,"a" ); User u2 = new User (2 ,25 ,"b" ); User u3 = new User (3 ,23 ,"c" ); User u4 = new User (4 ,15 ,"d" ); User u5 = new User (5 ,16 ,"e" ); List<User> list = Arrays.asList(u1,u2,u3,u4,u5); System.out.println(list); list.stream() .filter(u->{return u.getId()%2 ==0 ;}) .filter(u->{return u.getAge() > 16 ;}) .map(u->{return u.getName().toUpperCase();}) .sorted((uu1,uu2) -> {return uu1.compareTo(uu2);}) .limit(1 ) .forEach(System.out::println); } }
ForkJoin ForkJoin在JDK1.7中,并行执行任务,提交效率,一般通用于大数据量!
大数据:Map Reduce(把大任务拆分为小任务)
ForkJoin特点
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class Test { public static void main (String[] args) throws ExecutionException, InterruptedException { test3(); } public static void test1 () { Long start = System.currentTimeMillis(); Long sum = 0L ; for (long i = 1L ; i <= 10_0000_0000 ; i++) { sum += i; } long end = System.currentTimeMillis(); System.out.println("Sum=" +sum+"时间\t" + (end -start)); } public static void test2 () throws ExecutionException, InterruptedException { Long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool (); ForkJoinTask<Long> task = new ForkJoinDemo (1L ,10_0000_0000L ); forkJoinPool.execute(task); ForkJoinTask<Long> submit = forkJoinPool.submit(task); Long sum = submit.get(); long end = System.currentTimeMillis(); System.out.println("Sum=" +"时间" + (end -start)); } public static void test3 () { Long start = System.currentTimeMillis(); Long sum = LongStream.rangeClosed(0L ,10_0000_0000L ).parallel().reduce(0 ,Long::sum); Long end = System.currentTimeMillis(); System.out.println("Sum=" +sum + "\n时间" + (end -start)); } }
异步回调
Future设计初衷,对将来某个事件结果进行建模
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class FutureDemo { public static void main (String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{ try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(Thread.currentThread().getName() + "RunAsny=>Void" ); }); System.out.println("111" ); completableFuture.get(); System.out.println("=================================" ); CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer" ); int i = 4 /0 ; return 1024 ; }); completableFuture1.whenComplete((t,u)->{ System.out.println( " --> " +t); System.out.println( " -- >" + u); }).exceptionally((e)->{ System.out.println(e.getMessage()); return 444 ; }).get(); } }
JVM初步理解
谈谈你对Volatile的理解
volatile是javaJVM提供的轻量级的同步机制
保证可见性
不保证原子性
静止指令重排
JMM:java内存模型,不存在的东西,就是一个概念!约定**
关于JMM的一些同步的约定
线程解锁前,必须把共享变量立刻 刷回主存。
线程加锁前,必须读取主存中的最新值到工作内存中。
加锁和解锁是同一把锁。
线程工作内存,主内存
jvm的8种操作:
read - > load
user -> assign
write ->store
lock -> unlock
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类 型的变量来说,load、store、read和write操作在某些平台上允许例外)
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量 才可以被其他线程锁定
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便 随后的load动作使用
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机 遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变 量副本中
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,
以便后续的write使用write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内 存的变量中
JMM对这八种指令的使用,制定了如下规则 :
不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须 write
不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
不允许一个线程将没有assign的数据从工作内存同步回主内存
一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量 实施use、store操作之前,必须经过assign和load操作
一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解 锁
如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前, 必须重新load或assign操作初始化变量的值
如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量 对一个变量进行unlock操作之前,必须把此变量同步回主内存
Volatile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class VolatileDemo { private static int num = 0 ; public static void main (String[] args) throws InterruptedException { new Thread ( ()->{ while (num == 0 ){ } }).start(); TimeUnit.SECONDS.sleep(1 ); num =1 ; System.out.println(num); } }
验证特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class VolatileDemo { private volatile static int num = 0 ; public static void main (String[] args) throws InterruptedException { new Thread ( ()->{ while (num == 0 ){ } }).start(); TimeUnit.SECONDS.sleep(1 ); num =1 ; System.out.println(num); } }
原子性: 线程A在执行任务的时候,不能被打扰到,也不能被分割,要么同时成功要么同时失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class VolatileDemo2 { private volatile static int num = 0 ; public static void add () { num ++; } public static void main (String[] args) { for (int i = 0 ; i < 20 ; i++) { new Thread (()->{ for (int j = 0 ; j < 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2 ){ Thread.yield (); } System.out.println(Thread.currentThread().getName() + " " + num); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class VolatileDemo2 { private volatile static int num = 0 ; private static Lock lock = new ReentrantLock (); public static void add () { lock.lock(); try { num ++; }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } public static void main (String[] args) { for (int i = 0 ; i < 20 ; i++) { new Thread (()->{ for (int j = 0 ; j < 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2 ){ Thread.yield (); } System.out.println(Thread.currentThread().getName() + " " + num); } }
如果不加Lock和synochronized如何保证原子性。
使用原子类解决原子性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class VolatileDemo2 { private volatile static AtomicInteger num = new AtomicInteger (); private static Lock lock = new ReentrantLock (); public static void add () { num.getAndIncrement(); } public static void main (String[] args) { for (int i = 0 ; i < 20 ; i++) { new Thread (()->{ for (int j = 0 ; j < 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2 ){ Thread.yield (); } System.out.println(Thread.currentThread().getName() + " " + num); }
这些类的底层都是和操作系统挂钩,在内存中修改中,Unsafe是一个特殊的存在。
指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的程序那样去执行的。
源代码 - — 编译器里边的优化的重排 ——— 指令并行也可能会重排 — 内存系统也会重排——-执行
1 2 3 4 5 int x = 1 ; int y = 2 ;x = x+ 5 ; y = x +x;
可能造成影响的结果: a b x y 这四个值 默认都是 0
线程A
线程B
x =a
y =b
b =1
a = 2
正常的结果 x = 0; y = 0;
volatile可以避免指令重排
内存屏障,CPU指令,作用:
保证特定的操作执行顺序
保证某些变量的内存可见性(利用这些特性Volatile实现可见性)
volatile可保证可见性,不能保证原子性,由于内存屏障,可以避免指令重排的现象产生。
内存屏障使用最多的场景是单例模式
单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class SingleDemo { private byte [] d1 = new byte [1024 * 1024 ]; private byte [] d2 = new byte [1024 * 1024 ]; private byte [] d3 = new byte [1024 * 1024 ]; private byte [] d4 = new byte [1024 * 1024 ]; private SingleDemo () { } private static SingleDemo singleDemo = new SingleDemo (); public static SingleDemo getInstance () { return singleDemo; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class LazyDemo { private LazyDemo () { System.out.println(Thread.currentThread().getName() + "\t OK" ); } private volatile static LazyDemo lazyDemo; public static LazyDemo getInstance () { if (lazyDemo == null ){ synchronized (LazyDemo.class){ lazyDemo = new LazyDemo (); } } return lazyDemo; } public static void main (String[] args) { for (int i = 0 ; i < 10 ; i++) { new Thread (()->{ LazyDemo.getInstance(); },String.valueOf(i)).start(); } } }
1 2 3 4 5 6 7 8 9 10 11 public class Holder { private Holder () { } public static Holder getInstace () { return InnerClass.HOLDER; } public static class InnerClass { private static final Holder HOLDER = new Holder (); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public enum EnumSingle { INSTANCE; public EnumSingle getInstance () { return INSTANCE; } } class Test { public static void main (String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { EnumSingle instance1 = EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int .class); declaredConstructor.setAccessible(true ); EnumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }
CAS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class CASDemo { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (2022 ); System.out.println(atomicInteger.compareAndSet(2022 , 2123 )); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2020 , 2021 )); System.out.println(atomicInteger.get()); } }
CAS:比较当前工作内存中的值和主内存的值,如果这个值是期望的,那么执行操作,如果不是就一直循环
缺点:
循环会耗时
一次性只能保证一个共享变量的原子性
ABA问题
CAS:ABA问题(狸猫换太子)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class CASDemo { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (2022 ); System.out.println(atomicInteger.compareAndSet(2022 , 2123 )); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2123 , 2022 )); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2022 , 2123 )); System.out.println(atomicInteger.get()); } }
原子引用(ABA问题) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class CASDemo2 { public static void main (String[] args) { AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference (1 ,1 ); new Thread (()->{ int stamp = atomicStampedReference.getStamp(); System.out.println( Thread.currentThread().getName()+ " " + atomicStampedReference.getStamp()); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(atomicStampedReference.compareAndSet( 1 , 5 , atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 )); System.out.println( Thread.currentThread().getName()+ " -A2- " + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet( 5 , 6 , atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1 )); System.out.println( Thread.currentThread().getName()+ " -A3- " + atomicStampedReference.getStamp()); },"A" ).start(); new Thread (()->{ int stamp = atomicStampedReference.getStamp(); System.out.println( Thread.currentThread().getName()+ " " + atomicStampedReference.getStamp()); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(atomicStampedReference.compareAndSet(2020 , 6666 , stamp, stamp + 1 )); System.out.println( Thread.currentThread().getName()+ " -B2- " + atomicStampedReference.getStamp()); },"B" ).start(); } }
锁机制的了解 公平锁(包含读写锁) 公平锁:非常公平,不能插队,必须先来先到
非公平锁:java自带默认非公平锁,可以插队
1 2 3 4 5 public ReentrantLock () { sync = new NonfairSync (); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class 公平锁 { private Lock lock = new ReentrantLock (); public static void main (String[] args) { Phone phone = new Phone (); new Thread (() -> { phone.sms(); },"A" ).start(); new Thread (()->{ phone.sms(); },"B" ).start(); } } class Phone { public synchronized void sms () { System.out.println(Thread.currentThread().getName()+"发短信" ); call(); } public synchronized void call () { System.out.println(Thread.currentThread().getName()+"打电话" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class LockDemo { private Lock lock = new ReentrantLock (); public static void main (String[] args) { Phone2 phone2 = new Phone2 (); new Thread (()->{ phone2.sms(); },"A" ).start(); new Thread (()->{ phone2.sms(); },"B" ).start(); System.out.println("=====================" ); Test te = new Test (); new Thread (()->{ te.sms(); },"C" ).start(); new Thread (()->{ te.sms(); },"D" ).start(); } } class Phone2 { private Lock lock = new ReentrantLock (); public void sms () { lock.lock(); try { System.out.println(Thread.currentThread().getName()+"发消息" ); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } call(); } public void call () { lock.lock(); try { System.out.println(Thread.currentThread().getName()+ "打电话" ); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } } class Test { public void sms () { System.out.println(Thread.currentThread().getName()+"发消息" ); call(); } public void call () { System.out.println(Thread.currentThread().getName()+ "打电话" ); } }
可重入锁 可重入锁是某个线程已经获得某个锁,可以再次获取锁而不会出现死锁 。再次获取锁的时候会判断当前线程是否是已经加锁的线程,如果是对锁的次数+1,释放锁的时候加了几次锁,就需要释放几次锁 。
代码中的锁的递归只是锁的一种表现及证明形式,除了这种形式外,还有另一种表现形式。同一个线程在没有释放锁的情况下多次调用一个加锁方法,如果成功,则也说明是可重入锁。
自旋锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class 自旋锁 { AtomicReference<Thread> atomicStampedReference = new AtomicReference <>(); public void myLock () { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "=> MY Lock" ); while (!atomicStampedReference.compareAndSet(null ,thread)){ } } public void myUnLock () { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "=> MY UnLock" ); atomicStampedReference.compareAndSet(thread,null ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Test_Self { public static void main (String[] args) { 自旋锁 t = new 自旋锁(); new Thread (()->{ t.myLock(); try { TimeUnit.SECONDS.sleep(3 ); } catch (Exception e) { throw new RuntimeException (e); } finally { t.myUnLock(); } },"A" ).start(); new Thread (()->{ t.myLock(); try { TimeUnit.SECONDS.sleep(1 ); } catch (Exception e) { throw new RuntimeException (e); } finally { t.myUnLock(); } },"B" ).start(); } }
悲观锁 总是以悲观的角度来看问题,当用户拿到锁之后,认为用户会修改
乐观锁 总是以乐观的方式看待问题,当用户每次拿到锁后,都不会对数据进行修改
死锁 两个线程强制一个资源造成的两个线程处于等待状态,在不经过人的干预的情况之下一直会保持这种状态,我们称之为死锁。
如何去避免死锁(避免死锁产生的条件)
如何去判断线程产生死锁问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class 死锁{ public static void main (String[] args) { String lockA = "lockA" ; String lockB = "lockB" ; new Thread (new MyTest (lockA,lockB),"A" ).start(); new Thread (new MyTest (lockB,lockA),"B" ).start(); } } class MyTest implements Runnable { private String lockA; private String lockB; public MyTest (String lockA, String lockB) { this .lockA = lockA; this .lockB = lockB; } @Override public void run () { synchronized (lockA){ System.out.println(Thread.currentThread().getName() + "Lock A " + "=> get " + lockB); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } synchronized (lockB){ System.out.println(Thread.currentThread().getName() + "Lock B " + lockB + "=> get " + lockA); } } } }
使用 jps -l
定位进程号
使用 jstack 进程号
查看进程的信息 ,找到死锁问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 PS E:\IDEAProgram \juc -learning> jstack 2400 2022-10-09 21:13:37 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.261-b12 mixed mode): "DestroyJavaVM" #1 4 prio=5 os_ prio=0 tid=0x000001f454e10800 nid=0x31ec wait ing on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "B" #1 3 prio=5 os_ prio=0 tid=0x000001f470d10800 nid=0x3690 waiting for moni tor entry [0x000000870e7ff000] java.lang.Thread.State: BLOCKED (on object monitor) at com.xh.juc.day07.锁.MyTest.run(死锁.java:40) - waiting to lock <0x0000000775b9f8a0> (a java.lang.String) - locked <0x0000000775b9f8d8> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "A" #1 2 prio=5 os_ prio=0 tid=0x000001f470d03000 nid=0x39e0 waiting for moni tor entry [0x000000870e6ff000] java.lang.Thread.State: BLOCKED (on object monitor) at com.xh.juc.day07.锁.MyTest.run(死锁.java:40) - waiting to lock <0x0000000775b9f8d8> (a java.lang.String) - locked <0x0000000775b9f8a0> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "Service Thread" #1 1 daemon prio=9 os_ prio=0 tid=0x000001f470ba4000 nid=0x3 ea0 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #1 0 daemon prio=9 os_ prio=2 tid=0x000001f470af0000 nid =0x3f94 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" #9 daemon prio=9 os_ prio=2 tid=0x000001f470aef000 nid= 0x3168 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_ prio=2 tid=0x000001f470ade000 nid= 0xd1c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_ prio=2 tid=0x000001f470add000 nid= 0x1d30 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_ prio=0 tid=0x000001f470adb000 nid= 0x268c runnable [0x000000870dffe000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116 ) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x0000000775c8d1c0> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x0000000775c8d1c0> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$ 1.run(AppMainV2. java:49) "Attach Listener" #5 daemon prio=5 os_ prio=2 tid=0x000001f46edb9800 nid=0xd 64 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_ prio=2 tid=0x000001f46ee1e000 nid=0 x10e8 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_ prio=1 tid=0x000001f454eb7000 nid=0x2ee0 in Object.wait() [0x000000870dcfe000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x0000000775a08ee0> (a java.lang.ref.ReferenceQueue$ L ock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked <0x0000000775a08ee0> (a java.lang.ref.ReferenceQueue$ Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$ FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_ prio=2 tid=0x000001f454eb0000 nid= 0x35e0 in Object.wait() [0x000000870dbff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x0000000775a06c00> (a java.lang.ref.Reference$ Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x0000000775a06c00> (a java.lang.ref.Reference$ Lock) at java.lang.ref.Reference$ ReferenceHandler.run(Reference.java:153) "VM Thread" os_ prio=2 tid=0x000001f454ead800 nid=0x1f50 runnable "GC task thread#0 (ParallelGC)" os_ prio=0 tid=0x000001f454e26000 nid=0x3f00 runnable "GC task thread#1 (ParallelGC)" os_ prio=0 tid=0x000001f454e28800 nid=0x3eb0 runnable "GC task thread#2 (ParallelGC)" os_ prio=0 tid=0x000001f454e29800 nid=0x3e84 runnable "GC task thread#3 (ParallelGC)" os_ prio=0 tid=0x000001f454e2d000 nid=0x3fb8 runnable "GC task thread#4 (ParallelGC)" os_ prio=0 tid=0x000001f454e32000 nid=0x3ca0 runnable "GC task thread#5 (ParallelGC)" os_ prio=0 tid=0x000001f454e32800 nid=0x3df8 runnable "GC task thread#6 (ParallelGC)" os_ prio=0 tid=0x000001f454e35800 nid=0x3630 runnable "GC task thread#7 (ParallelGC)" os_ prio=0 tid=0x000001f454e36800 nid=0x8ec runnable "VM Periodic Task Thread" os_ prio=2 tid=0x000001f470be6000 nid=0x21f4 waiti ng on condition JNI global references: 12 Found one Java-level deadlock: ============================= "B": waiting to lock monitor 0x000001f454eb3c68 (object 0x0000000775b9f8a0, a java.lang.String), which is held by "A" "A": waiting to lock monitor 0x000001f454eb65a8 (object 0x0000000775b9f8d8, a java.lang.String), which is held by "B" Java stack information for the threads listed above: =================================================== "B": at com.xh.juc.day07.???.MyTest.run(死锁.java:40) - waiting to lock <0x0000000775b9f8a0> (a java.lang.String) - locked <0x0000000775b9f8d8> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "A": at com.xh.juc.day07.锁.MyTest.run(死锁.java:40) - waiting to lock <0x0000000775b9f8d8> (a java.lang.String) - locked <0x0000000775b9f8a0> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
如何检测死锁:
查看日志
堆栈信息
轻量锁 ① 当一个线程要进入同步块时,首先会创建一个Lock Record(锁记录)对象,该对象包含Displaced Mark Word和Owner。 ② 锁记录对象会拷贝对象头中的Mark Word,即Displaced Mark Word。 ③ 拷贝成功后,锁记录将owner指向对象头,然后尝试通过cas将对象头的Mark Word更改为指向Lock Record的指针,并且将对象Mark Word的锁标志位设置为“00”。 ④ 如果更新成功,则表示该对象处于轻量级锁定状态。如果失败,那么首先检测是否是可重入,如果可重入则进入执行。 ⑤ 如果不可重入,则膨胀为重量锁。(有的文章说会进行自旋,有的说不进行自旋只要存在竞争就膨胀为重量锁,美团说当只有一个等待线程时,该线程自旋。当超过一定次数或者超过一个自旋线程时直接膨胀为重量锁)。 ⑥ 释放锁时,使用cas对象头的Mark Word恢复,如果cas成功,则解锁成功。如果cas失败,则进入重量锁解锁流程。
重量锁 JDK1.6之前,锁没被优化,synchronized使用的是重量锁。重量锁需要操作系统帮忙,所以需要进行用户态到内核态的切换,同时还会带来线程上下文切换。 原理
Monitor对象分为3部分组成:Owner、EntryList和WaitSet ① JVM会创建一个Monitor对象,然后将锁对象的对象头中MarkWord改变成指向Monitor对象的指针。 ② 当其中一个线程抢占到锁后,Monitor的Owner会置为该线程id,只能有一个。 ③ 其余没抢到的,会被放置到EntryList中阻塞。 ④ 进入WAITING状态的线程,会被放在WaitSet中。
排他锁 排他锁(EXclusive Lock),又称X锁、独占锁、写锁。针对行锁。 当有事务对数据加写锁后,其他事务不能再对锁定的数据加任何锁,又因为InnoDB对select语句默认不加锁,所以其他事务除了不能写操作外,照样是允许读的(尽管不允许加读锁)。
📢主要为了在事务进行写操作时,不允许其他事务修改。
偏向锁 轻量锁每次重入和退出时还是会执行cas操作,并且要创建锁记录对象。如果在大多数情况下,锁总是由一个线程多次获得,不存在多线程竞争,就可以使用偏向锁优化。 原理 ① 第一次使用CAS将线程ID设置到对象的Mark Word。 ② 以后该线程进入同步块时,不需要CAS进行加锁,只会往当前线程的栈中添加一条Displaced Mark Word为空的Lock Record中,用来统计重入的次数。