赞
踩
JUC就是java.util.concurrent工具包的简称
进程
一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。
线程
进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
java默认有2个线程 mian线程 gc线程
并行和并发有什么区别?
并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。
并行:单位时间内,多个处理器或多核处理器同时处理多个任务,是真正意义上的“同时进行”。
串行:有n个任务,由一个线程按顺序执行。由于任务、方法都在一个线程执行所以不存在线程不安全情况,也就不存在临界区的问题。
做一个形象的比喻:
并发 = 两个队列和一台咖啡机。
并行 = 两个队列和两台咖啡机。
串行 = 一个队列和一台咖啡机。
并发变成的本质: 充分利用CPU资源
NEW //新生
RUNNABLE,// 运行
BLOCKED,//阻塞
WAITING,// 等待,死等
TIMED_WAITING,// 超时等待
TERMINATED;// 终止
来自不同的类
wait --> Object
sleep --> Thread
锁的释放
wait 会释放锁
sleep 抱着锁睡觉,不会释放锁
使用的范围不同
wait 必须在同步代码块
sleep 可以在任何地方睡
是否需要捕获异常
wait 需要捕获异常wait需要被唤醒,
sleep 须捕获异常 ,sleep不需要被唤醒
传统synchronized
公平锁: 十分公平 , 可以先来后到
非公平锁: 不公平 , 可以插队 (默认使用)
- package com.juc;
-
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- //买票
- public class SaleTickDemo2 {
- public static void main(String[] args) {
- // 多线程操作
- Ticket2 ticket = new Ticket2();
-
- // Runnable ; @FunctionalInterface 函数式接口 lambda (参数) - > {代码}
- new Thread( ()-> {
- for (int i = 0; i < 60 ; i++) {
- ticket.sale();
- }
- },"A").start();
- new Thread( ()-> {
- for (int i = 0; i < 60 ; i++) {
- ticket.sale();
- }
- },"B").start();
- new Thread( ()-> {
- for (int i = 0; i < 60 ; i++) {
- ticket.sale();
- }
- },"C").start();
- }
- }
- /*
- * 1. new ReentrantLock()
- * 2. lock.lock(); 加锁
- * 3.finally lock.unlock();解锁
- * */
- class Ticket2 {
- private int num = 50;
-
- Lock lock = new ReentrantLock();
-
- // 买票的方法
- public void sale(){
- lock.lock(); //加锁
- try {
- // 业务代码
- if (num > 0) {
- System.out.println(Thread.currentThread().getName() + "第几张" + (num--) + "剩余" + num + "张");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }finally {
- // 解锁
- lock.unlock();
- }
- }
- }
Synchronized 与 lock 区别
Synchronized 内置java关键字, lock是java的一个类
Synchronized 无法判断获取锁的状态, lock可以判断是否获取到了锁
Synchronized 会自动释放锁 , lock必须手动释放锁, 不释放锁会导致死锁
Synchronized 线程1 获得锁 线程2 等待 线程1阻塞线程2 会死等; lock 不一定会等待 lock.tryLock(); 尝试获取锁
Synchronized 可重入锁,不可中断,非公平. lock 可重入锁,可以判断锁 ,锁的公平(可以自定义)
Synchronized 适合少量代码同步问题, lock 适合大量代码同步
- package com.pc;
- /*
- * 线程之间的通信问题,生产者和消费者问题 等待唤醒 通知唤醒
- * 线程交替执行 A B 操作同一变量*/
- public class A {
- public static void main(String[] args) {
-
- Data date = new Data();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.increment();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"A").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.decrement();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"B").start();
- }
-
-
- }
- // 等待 业务 通知
- class Data{
- private int number = 0;
-
- public synchronized void increment() throws InterruptedException {
- // +1
- if (number != 0) {
- // 等待
- this.wait();
- }
- number++;
- System.out.println(Thread.currentThread().getName()+" "+number);
- // 通知其他线程 我 +1 完毕了
- this.notifyAll();
- }
- public synchronized void decrement() throws InterruptedException {
- // -1
- if (number == 0) {
- //等待
- this.wait();
- }
- number--;
- System.out.println(Thread.currentThread().getName()+" "+number);
- // 通知其他线程 我-1 完毕了
- this.notifyAll();
- }
- }
如果有 ABCD四个线程, 会出现虚假唤醒的情况 此时, 将if判断改为while 判断
- package com.pc;
- /*
- * 线程之间的通信问题,生产者和消费者问题 等待唤醒 通知唤醒
- * 线程交替执行 A B 操作同一变量*/
- public class A {
- public static void main(String[] args) {
-
- Data date = new Data();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.increment();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"A").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.decrement();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"B").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.increment();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"C").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.decrement();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"D").start();
- }
-
- }
-
- // 等待 业务 通知
- class Data{
- private int number = 0;
-
- public synchronized void increment() throws InterruptedException {
- // +1
- while (number != 0) {
- // 等待
- this.wait();
- }
- number++;
- System.out.println(Thread.currentThread().getName()+" "+number);
- // 通知其他线程 我 +1 完毕了
- this.notifyAll();
- }
- public synchronized void decrement() throws InterruptedException {
- // -1
- while (number == 0) {
- //等待
- this.wait();
- }
- number--;
- System.out.println(Thread.currentThread().getName()+" "+number);
- // 通知其他线程 我-1 完毕了
- this.notifyAll();
- }
- }
JUC版的生产者消费者
通过Lock 找到 Condition
- package com.pc;
-
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class B {
- public static void main(String[] args) {
-
- Data2 date = new Data2();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.increment();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"A").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.decrement();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"B").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.increment();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"C").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- try {
- date.decrement();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- },"D").start();
-
- }
- }
-
- // 等待 业务 通知
- class Data2{
- private int number = 0;
- Lock lock = new ReentrantLock();
- Condition condition = lock.newCondition();
- // condition.wait(); // 等待
- // condition.signalAll(); // 唤醒
-
- public void increment() throws InterruptedException {
- lock.lock();
- try {
- // 业务代码
- // +1
- while (number != 0) {
- condition.await(); // 等待
- }
- number++;
- System.out.println(Thread.currentThread().getName()+"-->"+number);
- // 通知其他线程 我 +1 完毕了
- condition.signalAll(); // 唤醒
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- public void decrement() throws InterruptedException {
- lock.lock();
- try {
- // -1
- while (number == 0) {
- condition.await();// 等待
- }
- number--;
- System.out.println(Thread.currentThread().getName()+"-->"+number);
- // 通知其他线程 我-1 完毕了
- condition.signalAll(); // 唤醒
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
不会出现虚假唤醒,但是 唤醒的没有规律
condition 精准通知和唤醒线程
- package com.pc;
-
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- /*
- * A执行完调用B
- * B执行完调用C
- * C执行完调用A
- * */
- public class C {
- public static void main(String[] args) {
- Data3 data3 = new Data3();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- data3.printA();
- }
- },"A").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- data3.printB();
- }
- },"B").start();
- new Thread(()->{
- for (int i = 0; i < 10; i++) {
- data3.printC();
- }
- },"C").start();
- }
- }
-
- class Data3{
- private Lock lock = new ReentrantLock();
- private Condition condition1 = lock.newCondition();
- private Condition condition2 = lock.newCondition();
- private Condition condition3 = lock.newCondition();
-
- private int number = 1;
- public void printA(){
- lock.lock();
- try {
- while (number != 1) {
- // 等待
- condition1.await();
- }
- System.out.println(Thread.currentThread().getName() + "--->A");
- // 唤醒指定的人
- // 业务代码 判断是否等待 执行 通知
- number = number+1;
- condition2.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- public void printB(){
- lock.lock();
- try {
- // 业务代码 判断是否等待 执行 通知
- while (number != 2) {
- condition2.await();
- }
- System.out.println(Thread.currentThread().getName() + "--->B");
- number = 3;
- condition3.signal();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- public void printC(){
- lock.lock();
- try {
- while (number != 3) {
- condition3.await();
- }
- System.out.println(Thread.currentThread().getName() + "--->C");
- number = 1;
- condition1.signal();
- // 业务代码 判断是否等待 执行 通知
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
怎么判断锁的是谁
new this 具体的都关系
static 锁的是Class
- package com.unsafe;
-
- import java.util.*;
- import java.util.concurrent.CopyOnWriteArrayList;
-
- /*
- * java.util.ConcurrentModificationException 并发修改异常
- * */
- public class ListTest {
- public static void main(String[] args) {
- // List<String> list = Arrays.asList("1", "12", "13");
- // list.forEach(System.out::println);
-
- // 并发下 ArrayList 不安全
- /*
- * 解决方法:
- * 1. List<String> list = new Vector<>();
- * 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
- * 3. List<String> list = new CopyOnWriteArrayList<>();
- * */
- // CopyOnWrite写入是复制 多个线程调用的时候 读取的是固定的,写入避免覆盖,写入的时候先copyof一份 ,最后在set回去
- // CopyOnWriteArrayList 比 Vector 厉害在哪
- 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();
- }
- }
- }
- package com.unsafe;
-
- import java.util.Collections;
- import java.util.HashSet;
- import java.util.Set;
- import java.util.UUID;
- import java.util.concurrent.ConcurrentSkipListSet;
-
- /*
- *解决方案:
- * 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
- * 2.Set<String> set = new ConcurrentSkipListSet<>();
- * */
- public class SetTest {
- public static void main(String[] args) {
- // Set<String> set = new ConcurrentSkipListSet<>();
- HashSet<Object> set = new HashSet<>();
- for (int i = 0; i < 30; i++) {
- new Thread(()->{
- set.add(UUID.randomUUID().toString().substring(0, 5));
- System.out.println(set);
- },String.valueOf(i)).start();
- }
- }
- }
hashSet底层是啥??
hashSet底层new 了一个HashMap
add方法 也是map.put()
- package com.unsafe;
-
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.UUID;
- import java.util.concurrent.ConcurrentHashMap;
-
- /*
- * java.util.ConcurrentModificationException
- * */
- public class MapTest {
- public static void main(String[] args) {
- // Map<String, String> map = new HashMap<>()
-
- Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
- // ConcurrentHashMap不能接受null的key和null的value,会抛出空指针异常
- // Map<String, String> map = new ConcurrentHashMap<>();
- for (int i = 1; i <= 30; i++) {
- new Thread(()->{
- map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
- System.out.println(map);
- },String.valueOf(i)).start();
- }
- }
- }
可以有返回值
可以抛出异常
方法不同 run() call()
- package com.callable;
-
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
-
- public class CallableTest {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- // new Thread(new Runnable()).start();
- // new Thread(new FutureTask<V>() ).start();
- // new Thread(new FutureTask<V>(Callable) ).start();
- new Thread().start(); // 怎么启动Callable
-
- MyThread myThread = new MyThread();
- FutureTask futureTask = new FutureTask(myThread);// 适配类
-
- new Thread(futureTask,"A").start();
- new Thread(futureTask,"B").start();
- // 两个线程,但只打印了一份 由于JVM第二次再调用FutrueTask对象所持有的线程, 此时FutrueTask的state此时已非NEW状态
- // 则此时会直接结束对应线程,就会导致任务也不执行,只是在第一次调用时返回结果保存了
- // 只执行一次,判断不是第一次就直接return了
- String o = (String)futureTask.get(); //获取Callable返回结果 get 方法可能会产生阻塞 或者使用异步通信处理
- System.out.println(o);
- }
- }
- class MyThread implements Callable<String>{ // Callable的泛型是啥返回值就是啥
-
- @Override
- public String call() {
- System.out.println("call");
- return "999";
- }
- }
- package com.add;
-
- import java.util.concurrent.CountDownLatch;
-
- //计数器
- public class CoundDownLatchDemo {
- public static void main(String[] args) throws InterruptedException {
- // 倒计时,总数是6
- CountDownLatch countDownLatch = new CountDownLatch(6);
- for (int i = 0; i < 6; i++) {
- new Thread(() -> {
- System.out.println(Thread.currentThread().getName() + "走了");
- countDownLatch.countDown(); // 数量减一
- },String.valueOf(i)).start();
- }
- countDownLatch.await();//等待计数器归零,然后向下执行
- System.out.println("关门");
- }
- }
原理:
countDownLatch.countDown(); // 数量减一
countDownLatch.await();//等待计数器归零
每次调用countDown() 数量减一 , 假设计数器变为0 countDownLatch.await() 就会被唤醒 ,继续执行
- package com.add;
-
- import java.util.concurrent.BrokenBarrierException;
- import java.util.concurrent.CyclicBarrier;
-
- //CyclicBarrier 执行达到指定线程数再执行操作
- //集齐7个葫芦娃合体
- 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 int temp = i;
- new Thread(() -> {
- System.out.println(Thread.currentThread().getName()+"召集了" + temp + "个葫芦娃");
- try {
- cyclicBarrier.await(); //等待
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (BrokenBarrierException e) {
- e.printStackTrace();
- }
- }).start();
- }
- }
- }
- package com.add;
-
- import java.util.concurrent.Semaphore;
- import java.util.concurrent.TimeUnit;
-
- //CyclicBarrier : 指定个数线程执行完毕再执行操作
- //CyclicBarrier 执行达到指定线程数再执行操作
- public class SemaohoreDemo {
- public static void main(String[] args) {
- // 线程数量 : 停车位 限流的时候可以用
- Semaphore semaphore = new Semaphore(3);
- for (int i = 1; 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 {
- semaphore.release(); // 释放
- }
- },String.valueOf(i)).start();
-
- }
- }
- }
semaphore.acquire(); 获得 ,假设已经满了,等待, 知道被释放为止
semaphore.release(); 释放 会将当前信号量释放 +1 , 然后唤醒等待的线程
作业 : 多个共享资源互斥使用 ; 并发限流(控制最大线程数)
- package com.rw;
-
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
-
- /*
- * ReadWriteLock
- * 独占锁(写锁) 一次只能被一个线程占有
- * 共享锁(读锁) 多个线程可以同时操作
- * 读 - 读 可以共存
- * 读 - 写 不能共存
- * 写 - 学 不能共存
- * */
- public class ReadWriteLockDemo {
- public static void main(String[] args) {
- MyCacheLock myCache = new MyCacheLock();
- for (int i = 1; i <= 5; i++) {
- final int temp = i;
- // 写入
- new Thread(() -> {
- myCache.put(temp+"",temp+"");
- },String.valueOf(i)).start();
- }
- for (int i = 1; i <= 5; i++) {
- final int temp = i;
- // 读取
- new Thread(() -> {
- myCache.get(temp+"");
- },String.valueOf(i)).start();
- }
- }
- }
-
- //自定义缓存
- // 会出现 写操作的时候被别的线程插队 , 所以引出ReadWriteLock
- 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() + "写入完成");
- }
- // 读 取
- 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 ReadWriteLock 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() + "写入完成");
- } 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();
- }
- }
- }
阻塞
队列
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put() | offer( , ,) |
移除 | remove | poll | take() | pool( , ) |
判断队列首 | element | peek | - | - |
- package com.bq;
-
- import java.util.concurrent.ArrayBlockingQueue;
-
- public class test {
- public static void main(String[] args) {
- test1();
- }
- /*
- * 抛出异常
- * */
- public static void test1(){
- // 队列的大小
- ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
- System.out.println(arrayBlockingQueue.add("a"));
- System.out.println(arrayBlockingQueue.add("c"));
- System.out.println(arrayBlockingQueue.add("v"));
- System.out.println("---------------");
- // java.lang.IllegalStateException: Queue full
- // System.out.println(arrayBlockingQueue.add("f"));
-
-
- // System.out.println(arrayBlockingQueue.element()); // 查看队首元素
- System.out.println(arrayBlockingQueue.remove());
- System.out.println(arrayBlockingQueue.remove());
- System.out.println(arrayBlockingQueue.remove());
-
- // java.util.NoSuchElementException
- // System.out.println(arrayBlockingQueue.remove());
- }
- }
- /*
- * 有返回值,没有异常*/
- 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(arrayBlockingQueue.peek()); //检测队首元素
- System.out.println(arrayBlockingQueue.poll());
- System.out.println(arrayBlockingQueue.poll());
- System.out.println(arrayBlockingQueue.poll());
- System.out.println(arrayBlockingQueue.poll()); // 返回 null 不抛出异常
- }
- /*
- * 等待 阻塞 (一直阻塞)
- * */
- public static void test3() throws InterruptedException {
- ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
- // 一直阻塞
- arrayBlockingQueue.put("a");
- arrayBlockingQueue.put("b");
- arrayBlockingQueue.put("c");
- // arrayBlockingQueue.put("d"); // 队列没有位置,一直阻塞
-
- System.out.println(arrayBlockingQueue.take());
- System.out.println(arrayBlockingQueue.take());
- System.out.println(arrayBlockingQueue.take());
- System.out.println(arrayBlockingQueue.take()); // 没有多余元素. 一直等待
- }
- /*
- * 等待 阻塞 (等待超时)
- * */
- 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("c",2, TimeUnit.SECONDS)); //等待超过两秒直接退出
-
- System.out.println(arrayBlockingQueue.poll());
- System.out.println(arrayBlockingQueue.poll());
- System.out.println(arrayBlockingQueue.poll());
- System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS)); //等待超过两秒直接退出
- }
没有容量
存进去一个元素后,必须等到取出来后才可以存入下一个元素
- package com.blockingQueue;
-
- import java.util.concurrent.BlockingQueue;
- import java.util.concurrent.SynchronousQueue;
- import java.util.concurrent.TimeUnit;
-
- /*同步队列
- * 和其他的BlockingQueue 不一样 ,SynchronousQueue 不存储元素
- * put 一个元素,必须先取出来 否则不能再put进去值 */
- public class SynchronizedQueueDemo {
- public static void main(String[] args) {
- BlockingQueue<String> 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) {
- e.printStackTrace();
- }
- },"A").start();
-
- new Thread(()->{
- try {
- TimeUnit.SECONDS.sleep(2);
- System.out.println(Thread.currentThread().getName() + "----" + synchronousQueue.take());
- TimeUnit.SECONDS.sleep(2);
- System.out.println(Thread.currentThread().getName() + "----" + synchronousQueue.take());
- TimeUnit.SECONDS.sleep(2);
- System.out.println(Thread.currentThread().getName() + "----" + synchronousQueue.take());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- },"B").start();
- }
- }
线程池三大方法 ; 七大参数 ; 四种拒绝策略
线程池的好处:
降低资源消耗
提高响应速度
方便管理
线程复用 , 可以控制最大并发,管理线程
池化技术
事先准备好一些资源,有人要用就来拿,用完之后归还
- package com.pool;
-
- import java.util.concurrent.Executor;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ThreadPoolExecutor;
-
- /*
- * Executors 工具类 3大方法
- * 使用线程池创建线程
- * */
- public class Demo1 {
- public static void main(String[] args) {
- // ExecutorService threadPoolExecutor= Executors.newSingleThreadExecutor();//当个线程
- // ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(5); //创建一个固定的线程池大小
- ExecutorService threadPoolExecutor = Executors.newCachedThreadPool(); //;可伸缩的 最大创建线程
-
- try {
- for (int i = 0; i < 10; i++) {
- // 使用线程池来创建线程
- threadPoolExecutor.execute(() -> {
- System.out.println(Thread.currentThread().getName() + " ok");
- });
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // 线程池使用完毕,程序结束,关闭线程池
- threadPoolExecutor.shutdown();
- }
-
- }
- }
- 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.corePoolSize = corePoolSize;
- this.maximumPoolSize = maximumPoolSize;
- this.workQueue = workQueue;
- this.keepAliveTime = unit.toNanos(keepAliveTime);
- this.threadFactory = threadFactory;
- this.handler = handler;
- }
- package com.pool;
-
- import java.util.concurrent.*;
- /*
- * java.util.concurrent.RejectedExecutionException 超出最大线程, 抛出异常
- * ThreadPoolExecutor.CallerRunsPolicy() 哪里来会那里去
- * ThreadPoolExecutor.DiscardPolicy() // 队列满了不会抛出异常
- * ThreadPoolExecutor.DiscardOldestPolicy() 队列满了尝试和 最早的竞争
- * */
- public class Demo2 {
- public static void main(String[] args) {
- ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
- 5,
- 3,
- TimeUnit.SECONDS,
- new LinkedBlockingDeque<>(3),
- Executors.defaultThreadFactory(),
- new ThreadPoolExecutor.DiscardOldestPolicy()); // 队列满了尝试和 最早的竞争
-
- try {
- // 最大线程数量= max + LinkedBlockingDeque
- for (int i = 1; i <= 9; i++) {
- // 使用线程池来创建线程
- threadPoolExecutor.execute(() -> {
- System.out.println(Thread.currentThread().getName() + " ok");
- });
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // 线程池使用完毕,程序结束,关闭线程池
- threadPoolExecutor.shutdown();
- }
- }
- }
IO密集型 CPU密集型
问题: 线程池最大线程怎么设置
- // 最大线程如何定义
- // 1.CPU 密集型 几核就是几, 可以保持CPU的效率最高
- System.out.println(Runtime.getRuntime().availableProcessors()); // 获取CPU核数
- // 2. IO 密集型 判断程序中消耗IO的线程,只要大于这些线程即可
- // 例 ; 程序中有15个大型任务, io特别占用资源 至少留15个线程处理这些任务
lambda表达式 ;链式编程 ;函数式接口 ;Stream流
函数时接口 : 只有一个方法的接口
- // 简化编程模型,新版本框架大量使用
- // foreach(消费者类的函数式接口)
- @FunctionalInterface
- public interface Runnable {
- public abstract void run();
- }
- package com.Function;
-
- import java.util.function.Function;
- /*
- * Function 函数式接口, 一个入参 一个输出
- * 是要是函数时接口 就可以用lambda表达式简化
- * */
- public class Demo {
- public static void main(String[] args) {
- // 输出输入的值
- // Function function = new Function<String, String>() {
- // @Override
- // public String apply(String s) {
- // return s;
- // }
- //
- // };
- Function<String, String> function = (str) -> {return str; };
- System.out.println(function.apply("123asd"));
- }
- }
有一个入参 返回值式布尔值
- package com.Function;
-
- import java.util.function.Predicate;
-
- /*
- * 断定型接口 有一个入参 返回值式布尔值
- * */
- public class Demo2 {
- public static void main(String[] args) {
- // 判断字符是否为空
- // Predicate<String> predicate = new Predicate<String>(){
- // @Override
- // public boolean test(String s) {
- // return s.isEmpty();
- // }
- // };
- Predicate<String> predicate = (srt) -> { return srt.isEmpty(); };
- System.out.println(predicate.test(""));
- }
- }
只有入参没有返回值
- package com.Function;
-
- import java.util.function.Consumer;
-
- /*
- * Consumer 消费型接口 只有输入 ,没有返回值
- * */
- public class Demo3 {
- public static void main(String[] args) {
- // Consumer<String> consumer = new Consumer<String>() {
- // @Override
- // public void accept(String str) {
- // System.out.println(str);
- // }
- // };
- // Consumer<String> consumer = (str)->{ System.out.println(str); };
- Consumer<String> consumer = System.out::println;
- consumer.accept("123qwe");
- }
- }
- package com.Function;
-
- import java.util.function.Supplier;
-
- /*
- * Supplier 没有参数 只有返回值
- * */
- public class Demo4 {
- public static void main(String[] args) {
- // Supplier<String> supplier = new Supplier<String>() {
- // @Override
- // public String get() {
- // return "123";
- // }
- // };
- Supplier<String> supplier = ()->{ return "123";};
- System.out.println(supplier.get());
- }
- }
集合 MySQL 存储的东西 交给Stream流来计算
- package com.stream;
-
- import java.util.Arrays;
- import java.util.List;
- /*
- * 要求筛选:
- * id必须式偶数
- * age 大于23
- * name 转换为大写
- * name 倒着排序
- * 只输出一个用户
- * */
- public class Test {
- 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(6,"e",25);
-
- List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
-
- list.stream()
- .filter(u -> {
- return u.getId() % 2 == 0;
- }).
- filter(u -> {
- return u.getAge() > 23;
- })
- .map(u -> {
- return u.getNamen().toUpperCase();
- }).sorted((uu1,uu2)->{
- return uu1.compareTo(uu2);})
- .limit(1)
- .forEach(System.out::println);
- }
- }
ForkJoin 再jDK1.7之后出的 , 并行执行任务 , 提高效率 处理大数据量时使用
ForkJoin 特点 任务窃取
里面维护的都是双端队列
代码测试
- package com.forkJoin;
-
-
- import java.util.concurrent.RecursiveTask;
-
- /*
- * 使用ForkJoin
- * 1. 通过ForkJoinPool执行
- * 2. 计算任务 forkJoinPool,execute(ForkJoinTxask task)
- * 3. 计算类继承 ForkJoinTxask
- * */
- public class ForkJoinDemo extends RecursiveTask<Long> {
- private long start;
- private long end;
-
- // 临界值
- private long temp = 10000L;
-
- public ForkJoinDemo(long start, long end) {
- this.start = start;
- this.end = end;
- }
-
- // 计算方法
- @Override
- protected Long compute() {
- if ((end - start) < temp) {
- long sum = 0L;
- for (long i = start; i <= end; i++) {
- sum += i;
- }
- return sum;
- } else {
- long midden = (start + end) / 2; // 中间值
- ForkJoinDemo task1 = new ForkJoinDemo(start, midden);
- task1.fork(); //拆分任务 ,任务压入队列
- ForkJoinDemo task2 = new ForkJoinDemo(midden + 1 , end);
- task2.fork();//拆分任务 ,任务压入队列
-
- return task1.join() + task2.join();
- }
- }
- }
测试类
- package com.forkJoin;
-
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.ForkJoinPool;
- import java.util.concurrent.ForkJoinTask;
- import java.util.stream.LongStream;
-
- public class Test {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- // test1(); //3820 239
- // test2(); //1996 121
- // test3(); //137 131
- }
-
- // 普通计算
- public static void test1() {
- long sum = 0;
- long start = System.currentTimeMillis();
- for (long i = 1; i <10_0000_0000 ; i++) {
- sum += sum;
- }
- long end = System.currentTimeMillis();
- System.out.println("时间:" + (end - start));
- }
- // 使用ForkJoin
- public static void test2() throws ExecutionException, InterruptedException {
- long start = System.currentTimeMillis();
-
- ForkJoinPool forkJoinPool = new ForkJoinPool();
- ForkJoinTask<Long> joinDemo1 = new ForkJoinDemo(0,10_0000_0000);
- ForkJoinTask<Long> submit = forkJoinPool.submit(joinDemo1);//提交任务
- Long sum = submit.get();
-
- long end = System.currentTimeMillis();
- System.out.println("sum:" + sum + "时间:" + (end - start));
- }
-
- // 使用Stream并行流
- public static void test3(){
- long start = System.currentTimeMillis();
- // Stream并行流
- long sum = LongStream.rangeClosed(0L, 10_0000_0000).parallel().reduce(0L, Long::sum);
- long end = System.currentTimeMillis();
- System.out.println("sum:" + sum + "时间:" + (end - start));
- }
- }
对未来的某个事件结果进行建模
- package com.future;
-
- import java.util.concurrent.CompletableFuture;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.TimeUnit;
-
- /*
- * 异步调用
- * 异步执行
- * 成功回调
- * 失败回调
- * */
- public class Demo1 {
- 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("1111");
- completableFuture.get();//阻塞获取执行结果
- */
- // 有返回值的异步回调
- CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
- System.out.println(Thread.currentThread().getName() + "---->supplyAsync");
- int i = 10 / 0;
- return 111;
- });
-
- System.out.println(completableFuture.whenComplete((u1, u2) -> {
- System.out.println("t: " + u1); // 正常返回结果java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero java.lang.ArithmeticException: / by zero
- System.out.println("u: " + u2);
- }).exceptionally((e) -> {
- System.out.println(e.getMessage());
- return 999; //可以获取到错误的返回结果
- }).get());
- }
- }
Volatile是java虚拟机提供的 轻量级同步机制
保证可见性
不保证原子性
禁止指令重排
JMM: java内存模型 , 是不存在的东西,是一种约定
关于JMM的约定:
1.线程解锁前 ,必须把共享变量立刻刷回主存
2.线程加锁前,必须读取主存中的最新值到内存中
3.加锁和解锁必须是同一把锁
线程 : 工作内存 主内存
内存交互操作有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操作之前,必须把此变量同步回主内存
问题: 程序不知道主内存已经被修改过了
- package com.testVolatile;
-
- import java.util.concurrent.TimeUnit;
-
- public class JMMDemo {
- // 不加volatile 程序就会死循环
- // 加上volatile 就能保证程序的可见性
- private volatile static int num = 0;
- public static void main(String[] args) {
-
- new Thread(() -> { //线程1 对主内存的变化不知道
- while (num == 0) {
-
- }
- }).start();
- try {
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- num = 1;
- System.out.println(num);
-
- }
- }
原子性: 不可分割
线程A再执行任务的时候,不能被打扰,不能被分割,要么同时成功,要么同时失败
- package com.testVolatile;
-
- //不保证原子性
- public class VolatileDemo {
- // volatile 不保证原子性
- private volatile static int num = 0;
- public static void add(){
- num++;
- }
- public static void main(String[] args) {
- // 理论上num结果为20000
- for (int i = 0; i < 20; i++) {
- new Thread(()->{
- for (int i1 = 0; i1 < 1000; i1++) {
- add();
- }
- }).start();
- }
- while (Thread.activeCount() > 2) { //默认两个线程开启 main gc
- Thread.yield();
- }
- System.out.println(Thread.currentThread().getName() + " " + num);
- }
- }
如果不加 lock和synchronized 怎么保证原子性
- package com.testVolatile;
-
- import java.util.concurrent.atomic.AtomicInteger;
-
- //不保证原子性
- public class VolatileDemo {
- // volatile 不保证原子性
- // 原子类的Integer
- private volatile static AtomicInteger num = new AtomicInteger();
- public static void add(){
- // num++; // 不是原子性操作
- num.getAndIncrement(); // CAS操作
- }
- public static void main(String[] args) {
- // 理论上num结果为20000
- for (int i = 0; i < 20; i++) {
- new Thread(()->{
- for (int i1 = 0; i1 < 1000; i1++) {
- add();
- }
- }).start();
- }
- while (Thread.activeCount() > 2) { //默认两个线程开启 main gc
- Thread.yield();
- }
- System.out.println(Thread.currentThread().getName() + " " + num);
- }
- }
这些类的底层直接和操作系统挂钩,再内存中修改,Unsafe是一个很特殊的存在!
指令重排: 你写的程序,计算机并不是按照你写的那样去执行的
源代码--> 编译器优化的重排--> 指令并行也可能回导致重排--> 内存系统也会重排--> 执行
处理器在进行指令重排的时候,会考虑数据之间的依赖性
内存屏障 CPU指令 作用:
保证特定的操作的执行顺序
可以保证某些变量的内存可见性(利用这些特性 Volatile实现了可见性)
饿汉式 DCL懒汉式
- package com.signal;
-
- //饿汉式
- public class Hungry {
-
- // 类加载的时候就将数组加载到内存,如果不使用,造成内存空间浪费
- private byte[] data1 = new byte[1024 * 1024];
- private byte[] data2 = new byte[1024 * 1024];
- private byte[] data3 = new byte[1024 * 1024];
- private byte[] data4 = new byte[1024 * 1024];
- private Hungry(){
-
- }
-
- private final static Hungry HUNGRY = new Hungry();
-
- public static Hungry getInstance() {
- return HUNGRY;
- }
- }
- package com.signal;
-
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
-
- //懒汉式单例
- public class LazyMan {
-
- private static boolean aaaa = false;
- private LazyMan(){
- synchronized (LazyMan.class) {
- if (aaaa == false) {
- aaaa = true;
- } else {
-
- throw new RuntimeException("反射获取结果");
- }
- }
- System.out.println(Thread.currentThread().getName()+" o k ");
- }
-
- // 加上volatile 避免 指令重排
- private volatile static LazyMan lazyMan;
-
- // 双重检查锁模式的 懒汉式 , 简称DCL懒汉式
- public static LazyMan getInstance(){
- if (lazyMan == null) {
- synchronized (LazyMan.class) {
- if (lazyMan == null) {
- lazyMan = new LazyMan(); //不是原子性操作
- /*
- * 1. 分配内存空间
- * 2.执行构造方法初始化对象
- * 3.把对象指向这个空间
- *
- * */
- }
- }
- }
- return lazyMan;
- }
-
- 多线程并发 加上volatile 后避免指令重排
- //public static void main(String[] args) {
- // for (int i = 0; i < 10; i++) {
- // new Thread(() -> {
- // LazyMan.getInstance();
- // }).start();
- //
- //
- // }
- //}
- // 反射
- public static void main(String[] args) throws Exception {
- // LazyMan instance = LazyMan.getInstance();
-
- Field aaaa = LazyMan.class.getDeclaredField("aaaa");
- aaaa.setAccessible(true);
-
- Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
- declaredConstructor.setAccessible(true);
- LazyMan instance1 = declaredConstructor.newInstance();
- aaaa.set(instance1, false);
-
- LazyMan instance2= declaredConstructor.newInstance();
-
- // System.out.println(instance);
- System.out.println(instance1);
- System.out.println(instance2);
- }
- }
- package com.signal;
-
- //静态内部类
- public class Holder {
- private Holder(){
-
- }
- public static Holder getInstance() {
- return InnerClass.HOLDER;
- }
- public static class InnerClass{
- private static final Holder HOLDER = new Holder();
- }
- }
- package com.signal;
-
- import java.lang.reflect.Constructor;
-
- //enum本身是一个Class类
- public enum EnumSingle {
-
- INSTANCE;
- public EnumSingle getInstamce() {
- return INSTANCE;
- }
-
-
- }
- class Test{
- public static void main(String[] args) throws Exception {
- 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.由于底层是自旋锁,循环回浪费时间
由于是底层CPU操作,一次不保证一个共享变量的原子性
- package com.cas;
-
- import java.util.concurrent.atomic.AtomicInteger;
-
- public class CASDemo {
- // CompareAndSwap : 比较并交换
- public static void main(String[] args) {
-
- AtomicInteger atomicInteger = new AtomicInteger(2020);
- // 如果期望值达到了 就更新,否则不更新 CAS是CPU的并发原语
- System.out.println(atomicInteger.compareAndSet(2020, 2021));
- System.out.println(atomicInteger.get());
- atomicInteger.getAndIncrement();
- System.out.println(atomicInteger.compareAndSet(2020, 2021));
- System.out.println(atomicInteger.get());
- }
- }
- package com.cas;
-
- import java.util.concurrent.atomic.AtomicInteger;
-
- public class CASDemo {
- // CompareAndSwap : 比较并交换
- public static void main(String[] args) {
-
- AtomicInteger atomicInteger = new AtomicInteger(2020);
- // 如果期望值达到了 就更新,否则不更新 CAS是CPU的并发原语
-
- // 捣乱线程
- System.out.println(atomicInteger.compareAndSet(2020, 2021));
- System.out.println(atomicInteger.get());
- // System.out.println(atomicInteger.getAndIncrement()+" 11111");
- System.out.println(atomicInteger.compareAndSet(2021, 2020));
- System.out.println(atomicInteger.get());
-
- // 期望线程
- System.out.println(atomicInteger.compareAndSet(2020, 6666));
- System.out.println(atomicInteger.get());
- }
- }
带版本号的原子操作 解决ABA问题 ,引入原子引用 对应的思想: 乐观锁
- package com.cas;
-
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicStampedReference;
-
- /*integer默认缓存-128->127,超过这个范围就要new对象了,就会分配新的地址,我们看到源码是==,非数值类型,我们比较的是对象的地址
- 我想看怎么找到问题所在的片段
- 1. 看compareAndSet的源码,里面是使用 == 进行比较的。
- 2. 由于new的时候声明泛型肯定是装箱类,这个时候传入值类型将会自动装箱
- 3. 自动装箱的后果就是地址不一致,使用==判断的结果就为false\
- 4. 总结:最好不使用原子类型,使用原子类型得保证比较时候传入的为同一个装箱类
- * */
- public class CASDemo2 {
- // CompareAndSwap : 比较并交换
- public static void main(String[] args) {
-
- // 正常情况下泛型中写的都是对象
- AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
-
- new Thread(() -> {
- int stamp = atomicInteger.getStamp(); //获得版本号
- System.out.println("A--->" + stamp);
- try {
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- 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("B1--->" + stamp);
- try {
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- System.out.println(atomicInteger.compareAndSet(1, 5, stamp, stamp + 1));
-
- System.out.println("B2--->" + atomicInteger.getStamp());
- }, "B").start();
-
- }
- }
integer默认缓存-128->127,超过这个范围就要new对象了,就会分配新的地址,我们看到源码是==,非数值类型,我们比较的是对象的地址
1. 看compareAndSet的源码,里面是使用 == 进行比较的。
2. 由于new的时候声明泛型肯定是装箱类,这个时候传入值类型将会自动装箱
3. 自动装箱的后果就是地址不一致,使用==判断的结果就为false\
4. 总结:最好不使用原子类型,使用原子类型得保证比较时候传入的为同一个装箱类
也叫递归锁
synchronized 锁
- package com.lock;
-
- //synchronized
- public class Demo1 {
- 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");
- }
- }
lock版
- package com.lock;
-
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class Demo2 {
- 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.lock() ,锁必须配对,否则回出现死锁
- lock.lock();
- try {
- System.out.println(Thread.currentThread().getName() + " sms");
- call();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- lock.unlock();
- }
- }
-
- public void call() {
- lock.lock();
-
- try {
- System.out.println(Thread.currentThread().getName() + " call");
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- }
自定义自旋锁
- package com.lock;
-
- import java.util.concurrent.atomic.AtomicReference;
-
- /*
- * 自旋锁
- * */
- public class SpinLockDemo {
- AtomicReference<Thread> atomicReference = new AtomicReference<>();
-
-
- // 加锁
- public void MyLock() {
- Thread thread = Thread.currentThread();
- System.out.println(thread.getName() + " MyLock");
- // 自旋锁
- while (!atomicReference.compareAndSet(null, thread)) {
-
- }
- }
-
- // 解锁
- public void MyUnLock(){
- Thread thread = Thread.currentThread();
- System.out.println(thread.getName() + " MyUnLock");
- atomicReference.compareAndSet(thread, null);
- }
- }
代码测试
- package com.lock;
-
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class TestSpinLock {
- public static void main(String[] args) throws InterruptedException {
- ReentrantLock reentrantLock = new ReentrantLock();
- reentrantLock.lock();
- reentrantLock.unlock();
-
- // 底层使用的自旋锁CAS
- SpinLockDemo lock = new SpinLockDemo();
- new Thread(() -> {
- lock.MyLock();
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.MyUnLock();
- }
- }, "T1").start();
- TimeUnit.SECONDS.sleep(2);
-
- new Thread(() -> {
- lock.MyLock();
- try {
- TimeUnit.SECONDS.sleep(1);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- lock.MyUnLock();
- }
- }, "T2").start();
- }
- }
T1进来是拿到了锁, 然后期望值null, 变成了Thread, 此时T1没有自旋, 而是跳出了循环,T2此时在无限循环, 过了等待时间, T1解锁, 变回了null, T2才能跳出循环
多个线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的场景,某一个同步块同时拥有“两个以上对象的锁”时,就可能发生“死锁”的问题。
- package com.lock;
-
- import java.util.concurrent.TimeUnit;
-
- public class DeadLockDemo {
- public static void main(String[] args) {
- String lockA = "lockA";
- String lockB = "lockB";
-
- new Thread(new MyThread(lockA,lockB),"A").start();
- new Thread(new MyThread(lockB,lockA),"B").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);
- }
- }
-
- }
- }
死锁解决方案:
产生死锁的四个必要条件:
破坏上述4个条件任意一个或多个条件就会避免死锁的发生
jps -l
使用 jps 定位进程号
如果命令不识别可以试试这个:
找到 C:\Users{用户名}\AppData\Local\Temp\hsperfdata_{用户名} 右键属性->安全->添加用户,添加自己用户名,并且打开所有权限
jstack 70680
对应进程号找问题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。