JUC并发编程 1.什么是JUC?
Java.util.Concurrent
Runnable没有返回值,效率比Callable低!
2.线程和进程
进程:一个程序
一个进程往往可以包括多个线程,至少包括一个。
java默认有两个进程:main和GC
线程:开了一个进程,Typora写字,自动保存(线程负责)
对于java而言,Thread、Runnable、Callable
java真的可以开启线程吗?不能,只能通过本地方法去调用底层的C++,无法直接操作。
3.并发、并行
并发、并行
并发(多线程操作同一个资源):
CPU一核,模拟出多条线程,快速交替。
1 2 //获取CPU的核数 System.out.println(Runtime.getRuntime().availableProcessors());
并发编程的本质:充分利用CPU的资源
并行(多个人一起走)
CPU多核,多个线程可以同时执行,线程池。
4.线程有六个状态 创建、运行、阻塞、等待、超时等待、终止
wait/sleep的区别
1.来自不同的类,wait是Object类,sleep是Thread类
2.关于锁的释放,wait会释放锁,sleep不会释放锁
3.使用的范围不同,wait必须在同步代码块中,sleep可以任何地方使用
4.是否需要捕获异常,wait不需要捕获异常,sleep必须捕获异常
5.Lock锁(重点)
传统的synchronized
线程就是一个单独的资源类,没有任何的附属操作 并发:多线程操作同一个资源,把资源丢到线程 jdk1.8 lambda表达式 ()->{}
Lock接口,实现类:可重用锁ReentrantLock、读锁,写锁
ReentrantLock源码:
1 2 3 4 5 6 public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
公平锁:先来后到
非公平锁:可以插队(默认)
1.new ReentrantLock
2.加锁 lock.lock()
3.解锁 lock.unlock();
Lock锁和synchronized区别:
1.synchronize是内置的java关键字,Lock是java类
2.synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
3.synchronized会自动释放锁,Lock锁必须要手动释放锁,不释放会导致死锁
4.synchronized线程1(获得锁,阻塞),线程2(等待,傻傻的等),Lock锁的线程2就不一定等待下去
lock.trylock():尝试获取锁
5.synchronized可重入锁,不可中断,非公平的;Lock可重入锁,可以判断锁,非公平(可以自己设置)
6.synchronized适合少量的代码同步问题,Lock适合锁大量的同步代码
锁是什么,如何判断锁的是谁?
6.生产者和消费者问题 面试:单例模式、排序算法、生产者和消费者、死锁
synchronized版本
wait()/notifyAll()
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 //线程间通信问题:生产者和消费者问题,等待唤醒,通知唤醒 //线程交替执行 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 (Exception e){ e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i=0;i<10;i++){ try { data.decrement(); }catch (Exception e){ e.printStackTrace(); } } }, "B").start(); } } //等待、业务、通知 class Data { //资源 private int num = 0; public synchronized void increment() throws InterruptedException { if (num != 0) { //等待 this.wait();//wait()方法会释放锁 } num++; //通知其他线程,+1完毕了 this.notifyAll(); System.out.println(Thread.currentThread().getName() + "=>" + num); } public synchronized void decrement() throws InterruptedException { if (num == 0) { //等待 this.wait(); } num--; //通知其他线程,-1完毕了 this.notifyAll(); System.out.println(Thread.currentThread().getName() + "=>" + num); } }
四个线程的synchronized
出现问题。
虚假唤醒:
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功
1.比如说买货,如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了 ,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁
被唤醒之后,它是从上次被挂起的地方,继续往下执行
因为if只会执行一次,执行完会接着向下执行if()外边的 而while不会,直到条件满足才会向下执行while()外边的
拿两个加法线程A、B来说,A执行一次后,A再执行,执行时调用了wait方法,那它会等待,此时会释放锁,之后线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行。
防止虚假唤醒。if改为while判断。
lock版本
await()/signal()
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 B { public static void main(String[] args) { Data2 data = new Data2(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.increment(); } catch (Exception e) { e.printStackTrace(); } } }, "C").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (Exception e) { e.printStackTrace(); } } }, "D").start(); } } //等待、业务、通知 class Data2 { //资源 private int num = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void increment() throws InterruptedException { lock.lock(); try { while (num != 0) { //等待 condition.await(); System.out.println(Thread.currentThread().getName() + "加阻塞"); } num++; //通知其他线程,+1完毕了 condition.signalAll(); System.out.println(Thread.currentThread().getName() + "=>" + num); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } // public void decrement() throws InterruptedException { lock.lock(); try { while (num == 0) { //等待 System.out.println(Thread.currentThread().getName() + "减阻塞"); condition.await(); } num--; //通知其他线程,-1完毕了 condition.signalAll(); System.out.println(Thread.currentThread().getName() + "=>" + num); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
任何一个新技术,绝对不仅仅只是覆盖原技术,应该有优势和补充。
Condition,精准的通知和唤醒线程
随机状态,需要有序执行
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 //线程A执行完通知线程B,线程B执行完通知C,线程C执行完通知A 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 num = 1; Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); public void printA() { lock.lock(); try { //业务,判断,执行,通知 while (num != 1) { //等待 condition1.await(); } System.out.println(Thread.currentThread().getName() + "=>AAA"); //唤醒指定 num = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock(); try { //业务,判断,执行,通知 while (num != 2) { //等待 condition2.await(); } System.out.println(Thread.currentThread().getName() + "=>BBB"); //唤醒指定 num = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock(); try { //业务,判断,执行,通知 while (num != 3) { //等待 condition3.await(); } System.out.println(Thread.currentThread().getName() + "=>CCC"); //唤醒指定 num = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
7.八锁现象
深刻理解锁:8锁:关于锁的八个问题
//8锁,关于锁的八个问题
问题1: 一个对象,标准情况下,两个线程打印是先发短信还是先打电话? synchronized 锁的对象是方法的调用者,两个方法用的是一个锁,谁先拿到谁先执行
问题2: 一个对象,sendMsg()延迟四秒,两个线程打印是先发短信还是先打电话? synchronized 锁的对象是方法的调用者, 两个方法用的是一个锁,谁先拿到谁先执行
问题3: 一个对象,增加了个普通方法hello(),两个线程打印是先发短信还是先hello? 普通方法没有锁,不是同步方法,不受锁的影响
问题4: 两个对象,分别调用sendMsg()和call(),两个线程打印是先发短信还是先打电话? 两把锁,按时间执行
问题5: 一个对象,sendMsg()和call()是静态的同步方法,两个线程打印是先发短信还是先打电话?static类一加载就有了,锁的是class,用的也是同一个锁
问题6: 两个对象,分别调用sendMsg()和call(),是静态的同步方法,两个线程打印是先发短信还是先打电话?static类一加载就有了,锁的是class,用的也是同一个锁
问题7: 一个对象,sendMsg()是静态同步方法,call()是非静态的同步方法,两个线程打印是先发短信还是先打电话?两把锁,按时间执行
问题8: 两个对象,分别调用sendMsg()是静态同步方法,call()是非静态的同步方法,两个线程打印是先发短信还是先打电话?两把锁,按时间执行
1.Test1
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 package com.xmx.锁; import java.util.concurrent.TimeUnit; //8锁,关于锁的八个问题 //问题1:标准情况下,两个线程打印是先发短信还是先打电话? //问题2:sendMsg()延迟四秒,两个线程打印是先发短信还是先打电话? public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone.call(); }, "B").start(); } } class Phone { //synchronized 锁的对象是方法的调用者 //两个方法用的是一个锁,谁先拿到谁先执行 public synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call() { System.out.println("打电话"); } }
2.Test2
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 package com.xmx.锁; import java.util.concurrent.TimeUnit; //8锁,关于锁的八个问题 //问题1:标准情况下,两个线程打印是先发短信还是先打电话? 两个方法用的是一个锁,谁先拿到谁先执行 //问题2:sendMsg()延迟四秒,两个线程打印是先发短信还是先打电话? 两个方法用的是一个锁,谁先拿到谁先执行 //问题3:增加了个普通方法hello(),两个线程打印是先发短信还是先hello? 普通方法没有锁,不是同步方法,不受锁的影响 //问题4:两个对象,分别调用sendMsg()和call(),两个线程打印是先发短信还是先打电话? 两把锁,按时间执行 public class Test2 { public static void main(String[] args) { Phone2 phone = new Phone2(); Phone2 phone2 = new Phone2(); new Thread(() -> { phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone2.call(); }, "B").start(); } } class Phone2 { //synchronized 锁的对象是方法的调用者 //两个方法用的是一个锁,谁先拿到谁先执行 public synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call() { System.out.println("打电话"); } //没有锁,不是同步方法,不受锁的影响 public void hello(){ System.out.println("hello"); } }
3.Test3
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 package com.xmx.锁; import java.util.concurrent.TimeUnit; //8锁,关于锁的八个问题 //问题1:一个对象,标准情况下,两个线程打印是先发短信还是先打电话? synchronized 锁的对象是方法的调用者,两个方法用的是一个锁,谁先拿到谁先执行 //问题2:一个对象,sendMsg()延迟四秒,两个线程打印是先发短信还是先打电话? synchronized 锁的对象是方法的调用者, 两个方法用的是一个锁,谁先拿到谁先执行 //问题3:一个对象,增加了个普通方法hello(),两个线程打印是先发短信还是先hello? 普通方法没有锁,不是同步方法,不受锁的影响 //问题4:两个对象,分别调用sendMsg()和call(),两个线程打印是先发短信还是先打电话? 两把锁,按时间执行 //问题5:一个对象,sendMsg()和call()是静态的同步方法,两个线程打印是先发短信还是先打电话?static类一加载就有了,锁的是class,用的也是同一个锁 //问题6:两个对象,分别调用sendMsg()和call(),是静态的同步方法,两个线程打印是先发短信还是先打电话?static类一加载就有了,锁的是class,用的也是同一个锁 public class Test3 { public static void main(String[] args) { Phone3 phone = new Phone3(); Phone3 phone2 = new Phone3(); new Thread(() -> { phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone2.call(); }, "B").start(); } } class Phone3 { //synchronized 锁的对象是方法的调用者 //两个方法用的是一个锁,谁先拿到谁先执行 public static synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public static synchronized void call() { System.out.println("打电话"); } //没有锁,不是同步方法,不受锁的影响 public void hello(){ System.out.println("hello"); } }
4.Test4
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 package com.xmx.锁; import java.util.concurrent.TimeUnit; //8锁,关于锁的八个问题 //问题1:一个对象,标准情况下,两个线程打印是先发短信还是先打电话? synchronized 锁的对象是方法的调用者,两个方法用的是一个锁,谁先拿到谁先执行 //问题2:一个对象,sendMsg()延迟四秒,两个线程打印是先发短信还是先打电话? synchronized 锁的对象是方法的调用者, 两个方法用的是一个锁,谁先拿到谁先执行 //问题3:一个对象,增加了个普通方法hello(),两个线程打印是先发短信还是先hello? 普通方法没有锁,不是同步方法,不受锁的影响 //问题4:两个对象,分别调用sendMsg()和call(),两个线程打印是先发短信还是先打电话? 两把锁,按时间执行 //问题5:一个对象,sendMsg()和call()是静态的同步方法,两个线程打印是先发短信还是先打电话?static类一加载就有了,锁的是class,用的也是同一个锁 //问题6:两个对象,分别调用sendMsg()和call(),是静态的同步方法,两个线程打印是先发短信还是先打电话?static类一加载就有了,锁的是class,用的也是同一个锁 //问题7:一个对象,sendMsg()是静态同步方法,call()是非静态的同步方法,两个线程打印是先发短信还是先打电话?两把锁,按时间执行 //问题8:两个对象,分别调用sendMsg()是静态同步方法,call()是非静态的同步方法,两个线程打印是先发短信还是先打电话?两把锁,按时间执行 public class Test4 { public static void main(String[] args) { Phone4 phone = new Phone4(); Phone4 phone2 = new Phone4(); new Thread(() -> { phone.sendMsg(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone2.call(); }, "B").start(); } } class Phone4 { //锁的是class类模板 public static synchronized void sendMsg() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } //锁的调用者 public synchronized void call() { System.out.println("打电话"); } //没有锁,不是同步方法,不受锁的影响 public void hello(){ System.out.println("hello"); } }
小结
new this 具体的一个对象
static 唯一的一个class模板
8.集合类不安全 List不安全
List不安全,并发运行出现ConcurrentModificationException并发修改异常问题
怎么解决?
1.Vector
2.工具类Collections有synchronizedList转换
1 2 3 4 5 6 7 8 9 10 11 12 public class ListTest { public static void main(String[] args) { List<String> list = Collections. (new ArrayList<>()); 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(); } } }
3.CopyOnwriteArrayList 写入时复制,COW,计算机程序设计领域的一种优化策略。
多线程在调用的时候,读取是固定的,写入为了避免覆盖,造成数据异常,就会复制一份。
CopyOnwriteArrayList 比 Vector 更好,只要有synchronized,效率就会比较低。
1 2 3 4 5 6 7 8 9 10 11 12 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不安全
ConcurrentModificationException 同理
解决方式:
1.工具类Collections有synchronizedSet转换
1 2 3 4 5 6 7 8 9 10 11 12 public class SetTest { public static void main(String[] args) { Set<String> set = Collections.synchronizedSet(new HashSet<>()); for (int i = 1; i < 10; i++) { new Thread(() -> { set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); },String.valueOf(i)).start(); } } }
2.CopyOnWriteArraySet
1 2 3 4 5 6 7 8 9 10 11 12 public class SetTest { public static void main(String[] args) { Set<String> set = new CopyOnWriteArraySet<>(); for (int i = 1; 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 public HashSet() { map = new HashMap<>(); }
1 2 3 4 public boolean add(E e) { //本质是HashMap的Key,无法重复的,PRESENT不变的值 return map.put(e, PRESENT)==null; }
Map不安全
ConcurrentModificationException
HashMap<String, String> map = new HashMap<>();默认等价HashMap<String, String> map = new HashMap<>(16,0.75f);工作中不这样用HashMap,
1 2 3 initialCapacity加载因子,loadFactor初始化容量 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final float DEFAULT_LOAD_FACTOR = 0.75f;
解决方式:
1.工具类Collections有synchronizedMap转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MapTest { public static void main (String[] args) { HashMap<String, String> map =Collections.synchronizedMap(new HashMap<>(16 , 0.75f )); for (int i = 1 ; i < 10 ; i++) { new Thread(() -> { map.put(Thread.currentThread() .getName(), UUID.randomUUID().toString().substring(0 , 5 )); System.out.println(map); }, String.valueOf(i)).start(); } } }
2.ConcurrentHashMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MapTest { public static void main(String[] args) { Map<String, String> map = new ConcurrentHashMap<>(); for (int i = 1; i < 10; i++) { new Thread(() -> { map.put(Thread.currentThread() .getName(), UUID.randomUUID().toString().substring(0, 5)); System.out.println(map); }, String.valueOf(i)).start(); } } }
9.Callable类(简单) 1.可以有返回值
2.可以抛出异常
3.方法不同,run()/call()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { //FutureTask是Runnable的实现类 MyThread thread=new MyThread(); FutureTask futureTask=new FutureTask(thread); new Thread(futureTask,"A").start(); System.out.println(futureTask.get());//获取返回结果 } } class MyThread implements Callable<String> { @Override public String call() throws Exception { System.out.println("call()"); return "123"; } }
10.常用辅助类 1.CountDownLatch减法计数器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //计数器(减法) public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch=new CountDownLatch(6);//总数 for (int i=1;i<=6;i++){ new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t出去"); countDownLatch.countDown();//-1 },String.valueOf(i)).start(); } countDownLatch.await();//等待计数器归零,再向下执行 System.out.println("关门"); } }
当计数为0,就会唤醒countDownLatch.await(),执行下面的代码
2.CyclicBarrier加法计数器 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 CyclicBarrierDemo { public static void main(String[] args) { //集齐七颗龙珠召唤神龙 CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{ System.out.println("召唤神龙成功"); }); for (int i=1;i<=7;i++){ //lambda表达式拿不到i,新建final变量就行(系统会自带加final关键字) final int temp=i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t收集了"+ temp +"颗龙珠"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } } }
3.Semaphore信号量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore=new Semaphore(3);//线程数量(停车位)/限流 for (int i=1;i<=6;i++){ new Thread(()->{ try { //得到acquire 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(); } } }
semaphore.acquire();获得,假如满了会等待,直到释放为止。
semaphore.release();释放,将当前的信号量释放+1,然后唤醒等待线程。
作用:多共享资源互斥,限流,控制最大线程数
11.读写锁(独占锁、共享锁)
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 public class ReadWriteLockDemo { public static void main(String[] args) { MyCache cache = new MyCache(); MyCacheLock myCacheLock=new MyCacheLock(); for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(() -> { // cache.put(temp + "", temp + ""); myCacheLock.put(temp + "", temp + ""); }, String.valueOf(i)).start(); } for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(() -> { // cache.get(temp + ""); myCacheLock.get(temp + ""); }, String.valueOf(i)).start(); } } } //自定义缓存 class MyCache { private volatile Map<String, Object> map = new HashMap<>(); //写 public void put(String key, Object value) { System.out.println(Thread.currentThread().getName() + "写入" + key); map.put(key, value); System.out.println(Thread.currentThread().getName() + "写入OK"); } //读 public void get(String key) { System.out.println(Thread.currentThread().getName() + "读取" + key); Object o = map.get(key); System.out.println(Thread.currentThread().getName() + "读取完成"); } } //自定义缓存:加锁 class MyCacheLock { private volatile Map<String, Object> map = new HashMap<>(); //读写锁,更加细粒度控制 private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //写,只有一个线程写 public void put(String key, Object value) { readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + "写入" + key); map.put(key, value); System.out.println(Thread.currentThread().getName() + "写入OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } } //读,多个线程同时读 public void get(String key) { readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "读取" + key); Object o = map.get(key); System.out.println(Thread.currentThread().getName() + "读取完成"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } }
12.阻塞队列BlockingQueue 不得不阻塞:写入:队列满了。读取:队列空的。
BlockingQueue接口(父类是Collection),Queue和List及Set同级,是Queue的子接口
使用场景:多线程并发处理,线程池
四组API
ArrayBlockingQueue
方式
抛出异常
有返回值
阻塞等待
超时等待
添加
add
offer
put
offer(xxx,2, TimeUnit.SECONDS)
移除
remove
poll
take
poll(xxx,TimeUnit.SECONDS)
判断队首元素
element
peek
1.抛出异常
1 2 3 4 5 6 7 8 9 10 11 public static void test1(){ ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); System.out.println(arrayBlockingQueue.add("a")); System.out.println(arrayBlockingQueue.add("b")); System.out.println(arrayBlockingQueue.add("c")); // System.out.println(arrayBlockingQueue.add("d")); System.out.println("============"); System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); System.out.println(arrayBlockingQueue.remove()); //System.out.println(arrayBlockingQueue.remove());}
1 System.out.println(arrayBlockingQueue.element());//查看队首元素
队列满了出现Queue full异常
队列为空出现NoSuchElementException异常
2.有返回值
1 2 3 4 5 6 7 8 9 10 11 12 public static void test2(){ ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); System.out.println(arrayBlockingQueue.offer("a")); System.out.println(arrayBlockingQueue.offer("b")); System.out.println(arrayBlockingQueue.offer("c")); // System.out.println(arrayBlockingQueue.offer("d")); //返回false System.out.println("============"); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); // System.out.println(arrayBlockingQueue.poll()); //返回null }
1 System.out.println(arrayBlockingQueue.peek());//查看队首元素
队列满了返回false
队列为空返回null
3.阻塞等待
1 2 3 4 5 6 7 8 9 10 11 12 13 //等待,阻塞(一直等待) public static void test3() throws InterruptedException { ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); arrayBlockingQueue.put("a"); arrayBlockingQueue.put("b"); arrayBlockingQueue.put("c"); arrayBlockingQueue.put("d");//队列没位置了,一直等待 arrayBlockingQueue.take(); arrayBlockingQueue.take(); arrayBlockingQueue.take(); arrayBlockingQueue.take();//没有元素,一直等待 }
4.超时等待
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //等待,阻塞(等待超时) public static void test4() throws InterruptedException { ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); System.out.println(arrayBlockingQueue.offer("a")); System.out.println(arrayBlockingQueue.offer("b")); System.out.println(arrayBlockingQueue.offer("c")); System.out.println(arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS));//超过两秒就退出 System.out.println(arrayBlockingQueue.peek()); System.out.println("============"); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.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 public class SynchronousQueueDemo { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); new Thread(() -> { try { System.out.println(Thread.currentThread().getName()+"put 1"); blockingQueue.put("1"); System.out.println(Thread.currentThread().getName()+"put 2"); blockingQueue.put("2"); System.out.println(Thread.currentThread().getName()+"put 3"); blockingQueue.put("3"); }catch (Exception e){ e.printStackTrace(); } }, "T1").start(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"==>"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"==>"+blockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"==>"+blockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }, "T2").start(); } }
13.线程池(重点) 3大方法、7大参数、4种拒绝策略
池化技术
事先准备好一些资源,需要使用就拿,用完归还。
程序的运行,本质:占用系统的资源
优化资源的使用=》池化技术
线程池、连接池、内存池、对象池。。。
创建和销毁浪费资源。
线程池的好处:
1.降低资源的消耗
2.提高响应的速度
3.方便管理
线程可以复用、可以控制最大并发数、管理线程
线程池:三大方法
三大方法 1 2 3 4 Executors.newSingleThreadExecutor(); //单个线程的线程池 Executors.newFixedThreadPool(5);//固定的线程池 Executors.newCachedThreadPool();//可伸缩的线程池
源码分析:
1 2 3 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())); }
1 2 3 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); }
1 2 3 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>()); }
均调用ThreadPoolExecutor
七个参数 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 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; }
四种拒绝策略 1.new ThreadPoolExecutor.AbortPolicy()
线程池和阻塞队列都满了,丢掉任务,抛出异常
2.new ThreadPoolExecutor.CallerRunsPolicy()
线程池和阻塞队列都满了,哪来的回哪去(由调用线程处理该任务)
3.new ThreadPoolExecutor.DiscardPolicy()
线程池和阻塞队列都满了,丢掉任务,不会抛出异常
4.new ThreadPoolExecutor.DiscardOldestPolicy()
线程池和阻塞队列都满了,丢弃队列最前面的任务,然后重新提交被拒绝的任务
手动创建线程池
最大承载:max+queue
1 2 超出最大承载 RejectedExecutionException//拒绝策略异常
最大线程到底该如何定义
最大线程到底该如何定义
1.CPU密集型
几核CPU就定义几,这样可以保持CPU效率最高。
获取CPU核数。
1 Runtime.getRuntime().availableProcessors()
2.IO密集型
判断程序中最耗IO的线程数,设置为它的两倍。
14.四大函数式接口(重点、简单)
程序员必须掌握的
1.lambda表达式 2.链式编程
3.函数式接口
4.Stream流式计算
函数式接口
1 2 3 4 5 @FunctionalInterface public interface Runnable { public abstract void run(); } 超多使用,简化编程模型,在新版本框架中大量应用
函数式接口都可以用lambda简写
1.Function函数型接口 有一个输入参数,有一个输出参数。
1 2 3 4 5 6 7 8 9 10 11 public static void main(String[] args) { //工具类,输出输入的值 // Function function=new Function<String,String>(){ // @Override // public String apply(String str) { // return str; // } // }; Function<String,String> function=(str)->{return str;}; System.out.println(function.apply("abc")); }
2.Predicate断定型接口 有一个输入参数,返回值只能是布尔值。
1 2 3 4 5 6 7 8 9 10 11 public static void main(String[] args) { //判断字符串是否为空 // Predicate predicate=new Predicate<String>() { // @Override // public boolean test(String str) { // return str.isEmpty(); // } // }; Predicate<String> predicate=(str)->{return str.isEmpty();}; System.out.println(predicate.test("")); }
3.Consumer消费型接口 只有输入,没有返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main(String[] args) { //打印字符串 // Consumer consumer=new Consumer<String>() { // @Override // public void accept(String str) { // System.out.println(str); // } // }; Consumer<String> consumer=(str)->{ System.out.println(str); }; consumer.accept("aaa"); }
4.Supplier供给型接口 没有参数,只有返回值
1 2 3 4 5 6 7 8 9 10 public static void main(String[] args) { // Supplier supplier=new Supplier<Integer>() { // @Override // public Integer get() { // return 1024; // } // }; Supplier supplier=()->{return 1024;} System.out.println(supplier.get()); }
15.Stream流式计算
什么是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 public static void main(String[] args) { User u1=new User(1,"a",21); User u2=new User(2,"b",22); User u3=new User(3,"c",23); User u4=new User(4,"d",24); User u5=new User(5,"e",25); User u6=new User(6,"F",26); //集合是存储 List<User> list= Arrays.asList(u1,u2,u3,u4,u5,u6); //计算交给stream //ID是偶数 //年龄大于23 //把用户名字都转为大写 //用户名倒置排序 //只输出一个用户 //链式编程 list.stream().filter((u)->{return u.getUid()%2==0;}) .filter((u)->{return u.getAge()>23;}) .map((u)->{return u.getUname().toUpperCase();}) .sorted((uu1,uu2)->{return uu2.compareTo(uu1);}) .limit(1) .forEach(System.out::println); }
16.ForkJoin
什么是ForkJoin
JDK1.7,并行执行任务,大数据量才能提高效率。
把大任务分成小任务,特点:工作窃取
双端队列
使用ForkJoin
1.ForkJoinPool执行
2.计算任务forkjoinpool.execute(ForkJoinTask task)
3.计算类继承RecursiveTask
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 //求和计算的任务(ForkJoin/Stream并行流) public class Test1 extends RecursiveTask<Long> { private Long start;//1 private Long end;//10 0000 0000 private Long temp=10000L; public Test1(Long start, Long end) { this.start = start; this.end = end; } //计算方法 @Override protected Long compute() { if(end-start>temp){ //分支合并计算 long middle= (start + end) / 2; Test1 test1 = new Test1(start, middle); test1.fork();//拆分任务,把任务压入线程队列 Test1 test2 = new Test1(middle+1, end); test2.fork(); return test1.join()+test2.join(); }else{ Long sum = 0L; for (Long i = start; i <= end; i++) { sum += i; } return sum; } } }
测试类
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 //普通程序员 //sum=500000000500000000 时间:10899 @Test public void a(){ 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)); } //ForkJoin会使用 //sum=500000000500000000时间:5132 @Test public void b() throws ExecutionException, InterruptedException { long start=System.currentTimeMillis(); ForkJoinPool forkJoinPool=new ForkJoinPool(); ForkJoinTask<Long> task=new Test1(0L,10_0000_0000L); // forkJoinPool.execute(task);//执行任务 Long sum=forkJoinPool.submit(task).get();//提交任务 long end=System.currentTimeMillis(); System.out.println("sum="+sum+"时间:"+(end-start)); } //Steam并行流 //sum=500000000500000000时间:1274 @Test public void c(){ 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+"时间:"+(end-start)); }
17.异步回调 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 public static void main(String[] args) throws ExecutionException, InterruptedException { //没有返回值的runAsync异步回调 // CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> { // try { // TimeUnit.SECONDS.sleep(2); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"runAsync=>Void"); // }); // System.out.println("11111"); // completableFuture.get();//阻塞获取执行结果 //有返回值得supplyAsync异步回调 //返回错误信息 CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + "runAsync=>Integer"); int i = 10 / 0; return 1024; }); System.out.println(completableFuture.whenComplete((t, u) -> { //正常得返回结果 System.out.println("t=>" + t); System.out.println("u=>" + u);//错误信息 }).exceptionally((e) -> { System.out.println(e.getMessage());//获取到错误得返回结果 return 233; }).get()); }
18.JMM
volatile是什么?
volatile是java虚拟机提供轻量级得同步机制。
1.保证可见性
2.不保证原子性
3.禁止指令重排
JMM
JMM:java内存模型,不存在得东西,概念、约定
关于JMM得一些同步约定:
1.线程解锁前、必须把共享变量立刻刷回主存。
2.线程加锁前,必须读取主存中得最新值到工作内存中。
3.加锁和解锁是同一把锁。
JMM中8种操作
1.read (读取)、2.load(载入)
3.use(使用)、 4.assign(赋值)
5.store(存储)、6.write(写入)
7.lock(锁定)、8.unlock(解锁)
线程A和线程B都读取了主存的值,但线程B修改值并写入主存,线程A还在使用原来的值,和主存的值不一致,相对于线程A,则没有可见性。
需要线程A指定主内存中的值发生了变化了。
volatile 保证可见性
保证可见性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private volatile static int num=0; public static void main(String[] args) { new Thread(()->{ while (num==0){ } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } num=1; System.out.println(num); }
不保证原子性
不保证原子性
原子性:不可分割
线程在执行任务的时候,不能被分割,要么同时成功,要么同时失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //不保证原子性 private volatile static int num=0; public static void add(){//synchronized可以保证原子性 num++; } public static void main(String[] args) { for (int i=1;i<=20;i++){ new Thread(()->{ for (int j=0;j<1000;j++){ add();//理论上nun为2万 } }).start(); } while (Thread.activeCount()>2){//main、GC线程默认执行 Thread.yield();//礼让,main线程主动礼让线程给其他线程执行 } System.out.println(Thread.currentThread().getName()+" "+num); }
如果没有synchronized或者lock,怎么保证原子性?
使用原子类,解决原子性问题
原子类为什么这么高级?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private volatile static AtomicInteger num=new AtomicInteger(); public static void add(){ //synchronized可以保证原子性 num.getAndIncrement();//CAS,在内存中修改值,加1操作 } public static void main(String[] args) { for (int i=1;i<=20;i++){ new Thread(()->{ for (int j=0;j<1000;j++){ add();//理论上nun为2万 } }).start(); } while (Thread.activeCount()>2){//main、GC线程默认执行 Thread.yield();//礼让,main线程主动礼让线程给其他线程执行 } System.out.println(Thread.currentThread().getName()+" "+num); }
这些类的底层直接和操作系统挂钩。
禁止指令重排
禁止指令重排
什么是指令重排?你写的程序,计算机并不是按照你写的那样去执行的。
处理器在进行指令重排的时候,会考虑数据之间的依赖性。
源代码->编译器优化重排->指令并行也可能重排->内存系统也会重排->执行
1 2 3 4 5 int x=1; //1 int y=2; //2 x=x+5; //3 y=x+x; //4 我们期望的1234,但是可能执行的时候变成2134,1324,不可能是 4123
可能造成影响的结果
正常结果:x=0,y=0,可能无依赖关系指令重排:
指令重排导致的结果:x=2,y=1
非计算机专业
volatile可以避免指令重排
内存屏障、CPU指令、作用:
1.保证特定的操作的执行顺序
2.保证某些变量的内存可见性(利用这些特性,volatile实现了可见性)
19.彻底玩转单例模式 1.构造函数私有
2.私有自身对象成员变量
3.公有获取对象方法
饿汉式
饿汉式
1 2 3 4 5 6 7 8 private Hungry(){} //可能会浪费空间 private byte[] data=new byte[1024*1024]; private final static Hungry Hungry=new Hungry(); public static Hungry getInstance(){ return Hungry; }
懒汉式
懒汉式
普通单线程,懒汉式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //单线程是ok的 private LazyMan(){ System.out.println(Thread.currentThread().getName()+"ok"); } private static LazyMan lazyMan; public static LazyMan getInstance(){ if(lazyMan==null){ lazyMan=new LazyMan(); } return lazyMan; }
DCL懒汉式 :双重检测模式的懒汉单例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private LazyMan() { System.out.println(Thread.currentThread().getName() + "ok"); } private static LazyMan lazyMan; //双重检测模式的懒汉单例,DCL懒汉式 public static LazyMan getInstance() { synchronized (LazyMan.class) { if (lazyMan == null) { lazyMan = new LazyMan(); } } return lazyMan; }
上面的代码存在问题,需要避免指令重排:volatile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private LazyMan() { System.out.println(Thread.currentThread().getName() + "ok"); } private volatile static LazyMan lazyMan;//禁止指令重排 //双重检测模式的懒汉单例,DCL懒汉式 public static LazyMan getInstance() { synchronized (LazyMan.class) { if (lazyMan == null) { lazyMan = new LazyMan();//不是一个原子性操作 //1.分配内存空间 //2.执行构造方法,初始化对象 //3.把这个对象指向这个空间 //正常123 //但如果走成1 3(期间单例B线程插入) 2 } } return lazyMan; }
静态内部类 1 2 3 4 5 6 7 private Holder(){} public static Holder getInstance(){ 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 //enum是什么,本身也是一个class类 public enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } } class Test{ public static void main(String[] args) { EnumSingle instance = EnumSingle.INSTANCE; } }
20.深入理解CAS
什么是CAS
1 2 3 4 5 6 7 8 //原子类的底层是CAS public static void main(String[] args) { AtomicInteger atomicInteger=new AtomicInteger(2021); //如果期望值达到了,就更新 //CAS是CPU的并发原理 atomicInteger.compareAndSet(2021,2022);//比较并交换 System.out.println(atomicInteger.get()); }
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则操作,如果不是就一值循环。
缺点:
1.循环耗时
2.一次只能保证一个共享变量的原子性
3.ABA问题
ABA问题 狸猫换太子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //原子类的底层是CAS public static void main(String[] args) { AtomicInteger atomicInteger=new AtomicInteger(2021); //如果期望值达到了,就更新 //CAS是CPU的并发原理 //对于SQL来说,会有乐观锁解决 //======================捣乱的线程============================== atomicInteger.compareAndSet(2021,2022);//比较并交换 System.out.println(atomicInteger.get()); atomicInteger.compareAndSet(2022,2021);//比较并交换 System.out.println(atomicInteger.get()); //=======================期望的线程============================ atomicInteger.compareAndSet(2021,2022);//比较并交换 System.out.println(atomicInteger.get()); }
怎么解决,不被捣乱?
21.原子引用 带版本号的原子!
如果泛型是包装类,要注意对象的引用问题。
Integer使用对象缓存机制,默认范围-128-127,推荐使用静态工厂方法valueof获取对象实例,而不是new,因为valueof使用缓存,而new一定会创建新的对象分配新的内存。
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 static void main(String[] args) { AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1,1); new Thread(()->{ int stamp = atomicInteger.getStamp(); //获得版本号 System.out.println("a=>"+stamp); System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("a2=>"+atomicInteger.getStamp()); System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("a3=>"+atomicInteger.getStamp()); },"a").start(); new Thread(()->{ int stamp = atomicInteger.getStamp(); //获得版本号 System.out.println("b=>"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicInteger.compareAndSet(1, 6, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("b2=>"+atomicInteger.getStamp()); },"b").start(); }
解决ABA问题,B线程把数据改变了又改回来,A线程不知情。
对应思想:乐观锁
22.各种锁的理解 1.公平锁、非公平锁 公平锁:非常公平,不能插队,线程必须先来后到! 非公平锁:非常不公平,可以插队(默认)
1 2 3 4 5 6 public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
2.可重入锁(递归锁) 拿到外面的锁,就可以自动获取里面的锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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()+"sms"); call();//这里也有锁 } public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"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 35 36 public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(() -> { phone.sms(); }, "A").start(); new Thread(() -> { phone.sms(); }, "B").start(); } } class Phone2 { Lock lock = new ReentrantLock(); public void sms() { lock.lock();//细节问题:两把锁,lock锁必须配对,不然会死锁 try { System.out.println(Thread.currentThread().getName() + "sms"); call();//这里也有锁 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void call() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "call"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
3.自旋锁 spinlock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //自旋锁 public class SpinLock1 { 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(); System.out.println(Thread.currentThread().getName()+"==>myunlock"); atomicReference.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 SpinLock1 spinLock = new SpinLock1(); new Thread(() -> { spinLock.myLock(); try { TimeUnit.SECONDS.sleep(3); }catch (Exception e){ e.printStackTrace(); }finally { spinLock.myUnLock(); } }, "T1").start(); new Thread(() -> { spinLock.myLock(); try { TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }finally { spinLock.myUnLock(); } }, "T2").start(); }
4.死锁
死锁是什么?
线程A抢线程B的锁,线程B抢线程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 29 30 31 32 33 34 public static void main(String[] args) { String lockA="lockA"; String lockB="lockB"; new Thread(new MyThread(lockA,lockB),"T1").start(); new Thread(new MyThread(lockA,lockB),"T2").start(); } } class MyThread implements Runnable { private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA){ System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get:"+lockB); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get:"+lockA); } } }
解决问题(死锁排查)
1.使用命令提示符命令,jps -l定位进程号
2.使用 jstack+进程号,找到死锁问题