JUC知识点记录

JUC知识点记录

看完狂神的视频 纯属想记录 回顾 狂神哥直链: 遇见狂神说的个人空间_哔哩哔哩_bilibili 没有不会做的事,只有不想做的事。

1、线程和进程

进程是操作系统中的应用程序、是资源分配的基本单位,线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位
一个进程往往可以包含多个线程,至少包含一个

线程

Java默认有几个线程?2个线程! main线程、GC线程

线程的状态

public enum State {<!-- -->

    	//运行
        NEW,

    	//运行
        RUNNABLE,

    	//阻塞
        BLOCKED,

    	//等待
        WAITING,

    	//超时等待
        TIMED_WAITING,

    	//终止
        TERMINATED;
    }

线程的生命周期

新建、就绪、运行、阻塞、死亡

进程

JAVA真的可以开启线程吗? 开不了的!

java是没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它可以调用底层的c++代码

    public synchronized void start() {<!-- -->
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {<!-- -->
            start0();
            started = true;
        } finally {<!-- -->
            try {<!-- -->
                if (!started) {<!-- -->
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {<!-- -->
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	//这是一个C++底层,Java是没有权限操作底层硬件的
    private native void start0();



并发

多线程操作同一个资源

  • CPU只有一核,模拟出来多个线程,天下武功唯快不破,那么就会造成一个CPU调度速度很快假象同时执行的,实则是交替执行,并发编程的本质就是充分的利用CPU资源

并行

并行:多个人行走

  • CPU多核的情况,多个线程同时执行,我们可以使用线程池!

    获取CPU的核数

    //获取CPU核数
    System.out.println(Runtime.getRuntime().availableProcessors());
    }
    

wait 跟sleep的区别

1、父类不同

​ wait => Object

​ sleep => Thread

一般企业中使用的休眠都是:

package com.neihan.dome;

import java.util.concurrent.TimeUnit;

public class CpuDome {
    public static void main(String[] args) {
        TimeUnit.SECONDS.sleep(1);  // SECONDS 是休眠的单位
    }
}

2、关于锁释放

wait: 会释放锁

sleep:不会释放锁

3、使用的范围是不同的

wait:必须在同步代码块中使用

sleep:可以在任意地方使用

Lock

公平锁:十分公平,必须先来后到,先进入的线程先执行

不公平锁:十分不公平,可以插队,当前面的线程需要执行的时间很长的时候,后面的线程可以插队**(默认不公平锁)**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-umyUkvZy-1636546509443)(C:\Users\Neihan\AppData\Roaming\Typora\typora-user-images\image-20211110143059977.png)]

Synchronized与Lock的区别

1、Synchronized 是java关键字,Lock是类

2、Synchronized 无法获取判断锁的状态,Lock可以判断

3、Synchronized 会自动释放锁,Lock必须手动加锁释放锁

4、Synchronized 线程 A (获得锁->阻塞)线程 B(等待) Lock 就不一定会等下去,Lock有一个trylock去尝试获得锁,不会造成长久的等待。

4、Synchronized 是可重入锁,不可以中断,非公平的,Lock 可重入,可以判断锁,可以自己设置公平锁非公平锁

5、 Synchronized 适合锁少量代码同步问题,Lock适合锁大量的同步代码

生产者消费者的关系

synchronized版本

if 判断有虚假唤醒,处理方法用 while

结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后

package com.neihan.pc;
public class ConsumeAndProduct {

    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();

    }
}

/*
    if 判断有虚假唤醒
    处理方法用 while
 */
class Data{

    Integer number = 0;
    
    public synchronized void increment() throws InterruptedException {
        //判断等待
       /* if(0 != number){
            this.wait();
        }*/

        while (0!= number){
            this.wait();
        }

        //操作
        number++;
        System.out.println(Thread.currentThread().getName()+"==>"+number);

        //通知
        this.notifyAll();

    }


    public synchronized void decrement() throws InterruptedException {

        //判断
  /*      if(0 == number){
            this.wait();
        }*/
        while (0== number){
            this.wait();
        }

        //操作
        number--;
        System.out.println(Thread.currentThread().getName()+"==>"+number);

        //通知
        this.notifyAll();
    }

}

Lock版本

package com.neihan.pc;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockCAP {

    public static void main(String[] args) {
        Data2 data = new Data2();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();


    }


}

class Data2{

    Integer number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public  void increment() throws InterruptedException {
        //加锁

        lock.lock();
        try {

            while (0!= number){
                //等待
                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 {

        //加锁
        lock.lock();
        try {

            while (0== number){
                //等待
                condition.await();
            }
            //操作
            number--;
            System.out.println(Thread.currentThread().getName()+"==>"+number);
            //通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

Condition的优势

指定唤醒线程

精准的通知和唤醒的线程!

如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程

package com.neihan.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 内涵
 * @Date: 2021/11/02/19:53
 * @Description:
 */
public class JUCPC {
    public static void main(String[] args) {
        Data3 data3 = new Data3();
        new Thread(()->{

            for (int i = 0; i < 10; i++) {
                try {
                    data3.printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data3.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data3.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
    }
}


class Data3{

    private Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2= lock.newCondition();
    Condition condition3= lock.newCondition();

    Integer number = 1;  // 1A 2B 3C

    public void printA() throws InterruptedException {

        //加锁
        lock.lock();

        try {
            while (number != 1){
                //等待
                condition1.await();
            }

            number = 2;

            System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
            //唤醒 B
            condition2.signal();
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }

    public void printB() throws InterruptedException {

        //加锁
        lock.lock();

        try {
            while (number != 2){
                //等待
                condition2.await();
            }

            number  =3 ;

            //唤醒 C
            System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBBB");
            condition3.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }

    public void printC() throws InterruptedException {

        //加锁
        lock.lock();

        try {
            while (number != 3){
                //等待
                condition3.await();
            }
            
            number = 1;
            
            //唤醒 A
            System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCCCCC");
            condition1.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

8锁现象

synchronized 锁的是当前的对象

package com.neihan.lock8;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/03/18:47
 * @Description:
 */
public class Test1 {

    public static void main(String[] args) throws InterruptedException {
        Send send1 = new Send();
        Send send2= new Send();

        new Thread(()->{

            try {
                send1.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();


        new Thread(()->{
            send2.sendEmail();
        },"B").start();

    }
}


class Send{

    /*
        synchronized 锁的是同一个对象
     */
    public synchronized void sendSms() throws InterruptedException {

        //增加延迟 1 s
        TimeUnit.SECONDS.sleep(1);

        System.out.println("发送短信");

    }

    public synchronized void sendEmail(){
        System.out.println("发送邮箱");
    }
}

static synchronized(静态同步函数)

静态同步函数锁的是类的 Class

package com.neihan.lock8;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/03/18:53
 * @Description: static synchronized 与 synchronized 的区别
 * 锁的对象不一样,static synchronized 锁的是类的Class 并且类Class全局唯一
 * synchronized 锁的是当前操作的对象,对象会有多个  俩者互不干扰
 */
public class Test2 {

    public static void main(String[] args) {
        Send2 send = new Send2();

        new Thread(()->{

            try {
                send.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();


        new Thread(()->{
            send.sendEmail();
        },"B").start();
    }


}

/**
 * 静态 synchronized 锁的是Class模板 全局唯一
 */
class Send2{

    /*
        synchronized 锁的是同一个对象
     */
    public static synchronized void sendSms() throws InterruptedException {

        //增加延迟 1 s
        TimeUnit.SECONDS.sleep(1);

        System.out.println("发送短信");

    }

    /*
        去掉static  锁的对象就不是同一个 所有发送邮箱先执行
     */
    public  synchronized void sendEmail(){
        System.out.println("发送邮箱");
    }

}

如果我们使用一个静态同步方法、一个同步方法、一个对象调用顺序是什么?

原因:因为一个锁的是Class类的模板,一个锁的是对象的调用者。所以不存在等待,直接运行。

集合不安全

List(不安全)

不安全的 触发 java.util.ConcurrentModificationException 异常 因为底层没有使用 synchronized

解决方案:

1、使用 new Vector<>()
2、Collections.synchronizedList(new ArrayList<>());
3、CopyOnWriteArrayList<>();

CopyOnWriteArrayList 比 Vector 好在哪?

Vector 和 CopyOnWriteArrayList 的区别
因为 Vector 使用了 synchronized 进行修饰,只要是使用了 synchronized 修饰的效率特别低下。
CopyOnWriteArrayList 没有使用 底层是使用的是 Lock锁 写入时复制 效率会更加高效! COW 计算机程序设计领域的一种优化策略

package com.neihan.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/03/19:39
 * @Description: 测试List、是否安全
 * 不安全的 触发 java.util.ConcurrentModificationException 异常 因为底层没有使用 synchronized
 */
public class TestList {

    public static  void main(String[] args) {


        List<String> list = new CopyOnWriteArrayList<>();

        /*
            解决方法
            1、使用 new Vector<>()
            2、Collections.synchronizedList(new ArrayList<>());
            3、CopyOnWriteArrayList<>();

            Vector 和 CopyOnWriteArrayList 的区别
            因为 Vector 使用了 synchronized 进行修饰,只要是使用了 synchronized 修饰的效率特别低下。
            CopyOnWriteArrayList 没有使用 底层是使用的是 Lock锁 写入时复制  效率会更加高效! COW 计算机程序设计领域的一种优化策略

         */

        for (int i = 1; i <=10; i++) {

            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            }).start();
        }
    }
}

Map(不安全)

默认加载因子是0.75,默认的初始容量是16

new HashMap<>(16,0.75);

package com.neihan.unsafe;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/8:35
 * @Description: 测试HashMap 是否线程安全
 * 不安全  触发 java.util.ConcurrentModificationException 线程修改异常
 *
 */
public class TestMap {

    public static void main(String[] args) {

        // 默认等价什么?new HashMap<>(16,0.75);

        //处理方式  Collections.synchronizedMap()
        //处理方式  new ConcurrentHashMap<>();

        Map<String, String> map = new ConcurrentHashMap<>();

        map.put("A","A");
        map.put("S","B");
        map.put("B","c");
        System.out.println(map);

       /* 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();
        }*/

    }
}


Set(不安全)

HashSet底层是什么?

hashSet底层就是一个HashMap

public HashSet() {
    map = new HashMap<>();
}
package com.neihan.unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/03/20:11
 * @Description: 测试 Set 是否安全
 * java.util.ConcurrentModificationException  并发修改 异常
 * Set 是不安全的
 *
 */
public class TestSet {

    public static void main(String[] args) {

        Set<Object> set = new  HashSet<>();

        /*
            解决方法
            1、Collections.synchronizedSet(new HashSet<>());
            2、new CopyOnWriteArraySet<>();
            hashSet 底层是什么?
            就是hashMap

            public HashSet() {
                map = new HashMap<>();
            }
            public boolean add(E e) {
                return map.put(e, PRESENT)==null;
            }

         */

        for (int i = 1; i <=20; i++) {

            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            }).start();
        }
    }
}

Callable

1、可以有返回值

2、可以抛出异常

3、方法不同 run() / call()

Callable

泛型就是返回值

启动需要 FutureTask 适配器 使得Callable 与 Runnable 有关联

package com.neihan.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/9:34
 * @Description:  测试 Callable  如何启动  需要什么适配器 进行启动 区别
 */
public class TestCallable {

    public static void main(String[] args) throws ExecutionException, InterruptedException {


        MyThread myThread = new MyThread();

        //使用 FutureTask 适配器 使得Callable 与 Runnable 有关联
        FutureTask futureTask = new FutureTask(myThread);

        new Thread(futureTask).start();

        //输出返回值  可能出现阻塞 可以把获取返回值放到最后一行,或者异步通信, 不阻塞 
        System.out.println(futureTask.get());

    }

}

class MyThread implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("call()");
        return 1024;
    }
}

三大工具类

CountDownLatch

new CountDownLatch(5);

创建的时候 指定初始值

package com.neihan.add;

import java.util.concurrent.CountDownLatch;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/10:07
 * @Description: 线程减法计数器
 */
public class CountDownLatchDemo {


    public static void main(String[] args) throws InterruptedException {


        CountDownLatch count = new CountDownLatch(5);

        for (int i = 1; i <=5; i++) {

            new Thread(()->{
                //减一
                count.countDown();
                System.out.println(Thread.currentThread().getName());
            },String.valueOf(i)).start();


        }

        //等待归零向下执行
        count.await();

        System.out.println("close");

    }
}

CyclicBarrier

package com.neihan.add;

import java.util.Hashtable;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/11:37
 * @Description:
 */
public class TestCyclicBarrier {

    public static void main(String[] args) {
        /**
        * @Description: 线程加法计数器
        * @Param: 1、需要到的次数,2、到达之后执行的线程
        * @return:
        * @Author: Neihan
        * @Date: 2021/11/4
        */
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("集结龙珠完成,召唤神龙");
        });


        for (int i = 1; i <=7; i++) {

            final Integer temp = i;
            new Thread(()->{

                System.out.println("第"+temp+"龙珠");

                try {
                    //等待次数到达
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            },String.valueOf(i)).start();

        }
    }
}

Semaphore

package com.neihan.add;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/14:48
 * @Description: Semaphore
 */
public class TestSemaphore {

    public static void main(String[] args) {

        /**
        * @Description:
        * @Param: [args]
        * @return:
        * @Author: Neihan
        * @Date: 2021/11/4
        */

        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);  //休眠 2s
                    System.out.println(Thread.currentThread().getName()+"离开到车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //释放
                    semaphore.release();
                }

            },String.valueOf(i)).start();

        }

    }
}

原理

semaphore.acquire(); 得到了资源,如果资源已经使用完,就等待资释放后再进行使用

semaphore.release(); 释放,会将当前的信号量释放 +1 然后唤醒等待的线程!

作用: 多个共享资源互斥的使用! 并发限流,控制最大的线程数!

读写锁

所以如果我们不加锁的情况,多线程的读写会造成数据不可靠的问题。

我们也可以采用synchronized这种重量锁和轻量锁 lock去保证数据的可靠。

但是这次我们采用更细粒度的锁:ReadWriteLock 读写锁来保证

package com.neihan.add;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/15:25
 * @Description: 读写锁
 *
 */
public class TestReadWriteLock {

    public static void main(String[] args) {
        MyCacheLock cache = new MyCacheLock();

        for (int i = 1; i <=10; i++) {
            final Integer temp = i;
            new Thread(()->{

                cache.write(temp+"",temp+"");

            },String.valueOf(i)).start();
        }

        for (int i = 1; i <=10; i++) {
            final Integer temp = i;
            new Thread(()->{
                cache.read(temp+"");

            },String.valueOf(i)).start();
        }


    }
}

class MyCache{

    private volatile Map<String,String> map = new HashMap<>();

    public void write(String key,String value){

        System.out.println(Thread.currentThread().getName()+"写入"+key);

        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入完成");

    }

    public void read(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取完成");
    }

}

class MyCacheLock{

    private volatile Map<String,String> map = new HashMap<>();
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();


    public void write(String key,String 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 read(String key){

        //读锁
        readWriteLock.readLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            readWriteLock.readLock().unlock();
        }
    }
}

阻塞队列

BlockQueue

是 Collection 的一个子类

什么情况下使用阻塞队列

多线程并发、线程池 的情况下使用

BlockQueue 有四组Api

1、抛出异常

2、不抛出异常,有返回值

3、一直阻塞

4、等待 超时阻塞

package com.neihan.queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/04/19:46
 * @Description:
 */
public class TestQueue {

    public static void main(String[] args) throws InterruptedException {
        test04();
    }


    /**
    * @Description: 抛出异常
     * java.lang.IllegalStateException: Queue full 异常   超过队列大小
     * java.util.NoSuchElementException 异常 队列没有元素
    * @Param: []
    * @return:
    * @Author: Neihan
    * @Date: 2021/11/4
    */
    public static void test01(){

        //队列需要初始化大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        // java.lang.IllegalStateException: Queue full 异常
        //System.out.println(blockingQueue.add("c"));


        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //java.util.NoSuchElementException 异常
        //System.out.println(blockingQueue.remove());


    }


    /**
    * @Description: 不抛出异常,有返回值
    * @Param: []
    * @return:
    * @Author: Neihan
    * @Date: 2021/11/4
    */
    public static void test02(){

        //队列需要初始化大小
        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("a"));


        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());


    }

    /**
    * @Description: 一直阻塞
    * @Param: []
    * @return:
    * @Author: Neihan
    * @Date: 2021/11/4
    */
    public static void test03() throws InterruptedException {

        //队列需要初始化大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        //一直阻塞不会返回
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");

        //如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
        //blockingQueue.put("d");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        //如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
        System.out.println(blockingQueue.take());
    }

    /**
    * @Description: 等待 超时阻塞
    * @Param: []
    * @return:
    * @Author: Neihan
    * @Date: 2021/11/4
    */
    public static void test04() throws InterruptedException {

        //队列需要初始化大小
        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",2, TimeUnit.SECONDS));


        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
        //取值,两秒没取到就不再等待
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
    }
}

同步队列

同步队列 没有 容量 也可以视为 容量为一的队列;

进去一个元素必须等进去的元素出来其他的元素才能进入

put方法 和 take方法;

SynchronousQueue 的take是使用了lock锁保证线程安全的。

package com.neihan.queue;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/8:43
 * @Description: 同步队列
 */
public class TestSynchronousQueue {

    public static void main(String[] args) {

        SynchronousQueue<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();
            }

        }).start();

        new Thread(()->{

            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(synchronousQueue.take()+"take()");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(synchronousQueue.take()+"take()");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(synchronousQueue.take()+"take()");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

    }
}

线程池

三大方式,七大参数,四种拒绝策略

池化技术

程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术

线程池、JDBC的连接池、内存池、对象池 等等。。。。

资源的创建、销毁十分消耗资源

池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。

线程池的好处

1、降低资源的消耗

2、提高响应速度

3、方便管理

实现线程复用、可以控制最大并发数、管理线程

package com.neihan.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/9:02
 * @Description: 线程池
 */
public class Dome01 {

    public static void main(String[] args) {
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();  //单个线程
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);  //固定多少个线程 或者说最大多少个线程
        ExecutorService threadPool = Executors.newCachedThreadPool();  //遇强则强,遇弱则弱


        try {
            for (int i = 0; i < 100; i++) {

                //使用线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+ " ok");
                });

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //使用完线程池一定要关闭
            threadPool.shutdown();
        }
    }
}

线程池的三大方法
  • ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单个线程
  • ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定多少个线程 或者说最大多少个线程
  • ExecutorService threadPool = Executors.newCachedThreadPool(); //遇强则强,遇弱则弱
线程池的七大参数

1、核心线程

2、最大线程

3、阻塞队列

4、线程工厂

5、超时数

6、超时单位

7、拒绝策略

/*
            线程池七大参数
                              int corePoolSize,   核心线程数
                              int maximumPoolSize,  最大线程池大小
                              long keepAliveTime,   超时了没人调用就会释放
                              TimeUnit unit,        超时单位
                              BlockingQueue<Runnable> workQueue  阻塞队列
                              ThreadFactory threadFactory,   线程工厂 创建线程的 一般不动
                              RejectedExecutionHandler handler 拒绝策略  四种拒绝策略
`
*/

拒绝策略

1. new ThreadPoolExecutor.AbortPolicy(): //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常

超出最大承载,就会抛出异常:队列容量大小+maxPoolSize

2. new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里 main线程进行处理

3. new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。

4. new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常

如何设置线程池的大小

1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小

I/O密集型:

在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。

四大函数式接口

新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算

Function 函数型接口

函数式接口 只有一个方法的接口 并带有 @FunctionalInterface

package com.neihan.function;

import java.util.function.Function;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/16:06
 * @Description: 函数式接口  只有一个方法的接口 并带有 @FunctionalInterface
 */
public class Demo01 {

    public static void main(String[] args) {

       /* Function<String, String> stringStringFunction = new Function<String,String>(){

            @Override
            public String apply(String o) {   //传入什么就返回什么
                return o;
            }
        };*/

        // lambda 表达式
        Function<String,String> function = (str)->{
            return str;
        };

        System.out.println(function.apply("neihan niu bi lambda"));

    }

}

断定型接口Predicate

断定型接口 只能传入一个参数,返回值只能是Boolean

package com.neihan.function;

import java.util.function.Predicate;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/16:22
 * @Description: 断定型接口  只能传入一个参数,返回值只能是Boolean
 */
public class Dome02 {

    public static void main(String[] args) {

     /*   Predicate<String> stringPredicate = new Predicate<String>(){
            @Override
            public boolean test(String o) {
                return o.isEmpty();
            }
        };*/

        Predicate<String> predicate = (str)->{ return str.isEmpty();};

        System.out.println(predicate.test(""));
    }
}

Supplier供给型接口

没有参数 只有返回值

package com.neihan.function;

import java.util.function.Supplier;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/16:39
 * @Description:  供给型接口  没有参数 只有返回值
 */
public class Demo03 {

    public static void main(String[] args) {
     /*   Supplier<String> stringSupplier = new Supplier<String>(){
            @Override
            public String get() {
                return "NeiHan";
            }
        };
*/

        Supplier supplier = ()->{return "Neihan Niu bi ";};
        System.out.println(supplier.get());
    }

}

消费性接口Consumer

消费性接口 只有参数没有返回值

package com.neihan.function;

import java.util.function.Consumer;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/17:03
 * @Description: 消费性接口 只有参数没有返回值
 */
public class Demo04 {
    public static void main(String[] args) {
        Consumer<String> stringConsumer = new Consumer<String>(){
            @Override
            public void accept(String o) {
                System.out.println(o);
            }
        };


        stringConsumer.accept("neihan ");
    }

}

stream流试计算

package com.neihan.function;

import java.util.Arrays;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/17:20
 * @Description: Stream 流式计算
 *
 *
 *
 * 题目要求 : 用一行代码实现
 * 1、ID 必须是偶数
 * 2、年龄必须是大于23
 * 3、用户名必须转换为大写
 * 4、用户名倒序
 * 5、只能输出一个用户
 *
 */
public class testStream {

    public static void main(String[] args) {

        Student student1 = new Student(16,"a",1);
        Student student2 = new Student(26,"b",2);
        Student student3 = new Student(25,"c",6);
        Student student4 = new Student(23,"d",4);

        List<Student> students = Arrays.asList(student1, student2, student3, student4);
                //链式编程  stream流式计算
                students.stream()
                .filter(student ->{return student.getId() % 2 ==0;})
                .filter(student -> {return student.getAge() > 23;} )
                .map(student -> {return student.getName().toUpperCase();})  //转换大写
                .sorted((s1,s2)->{return s2.compareTo(s1);})   //排序 倒叙输出
                .limit(1)   // 分页,显示的页数
                .forEach(System.out::printf);
    }
}

package com.neihan.function;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/06/18:44
 * @Description: Stream 案例
 * 取出数组中所有的偶数,并拼接成最大的偶数,
 */
public class StreamHome01 {

    public static void main(String[] args) {

        List<Integer> integers = Arrays.asList(2,3,4,6,60,7);
        String collect = integers
                .stream()
                .map(Object::toString)
                .sorted((s1, s2) -> {
                    return s2.compareTo(s1);
                })

                .filter((s) -> {
                    return Integer.parseInt(s) % 2 == 0;
                })
                .collect(Collectors.joining(""));

        String sz [] = new String[collect.length()];

        for (int i = 0; i < collect.length(); i++) {
            sz[i] = collect.substring(i,i+1);
        }

        Arrays.stream(sz)
                .sorted((i1,i2)->{return i2.compareTo(i1);})
                .forEach(System.out::printf);
    }
}

ForkJoin

ForkJoin 在JDK1.7,并行执行任务!提高效率~。在大数据量速率会更快!

大数据中:MapReduce 核心思想->把大任务拆分为小任务!

如何使用ForkJoin?

  • 1、通过ForkJoinPool来执行 - 2、计算任务 execute(ForkJoinTask<?> task) - 3、计算类要去继承ForkJoinTask; ForkJoin 的计算类
package com.neihan.forkjoin;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/08/14:17
 * @Description:
 */
@Data

@NoArgsConstructor
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{
            //使用Forkjoin计算
            long middle = (start + end) / 2;
            ForkJoinDemo forkJoin1 = new ForkJoinDemo(start,middle);
            //拆分任务 把任务压入队列
            forkJoin1.fork();
            ForkJoinDemo forkJoin2 = new ForkJoinDemo(middle+1,end);
            forkJoin2.fork();

            return forkJoin1.join() + forkJoin2.join();
        }
    }
}

测试类

package com.neihan.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/08/14:34
 * @Description: ForkJoin求和案例
 *
 *  3000 6000 90000
 */
public class TestForkjoin {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //test01(); 2928
        test02();  //4043
        //test03(); //198
    }

    private static void test01(){
        long start  = System.currentTimeMillis();
        long sum = 0L;

        for (Long i = 1L; i <= 10_0000_0000L; i++) {

            sum+= i;

        }
        Long end = System.currentTimeMillis();

        System.out.println("sum= "+sum+"时间:"+(end-start));

    }

    public static void test02() throws ExecutionException, InterruptedException {

        Long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);

        ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务

        Long sum = submit.get();

        Long end = System.currentTimeMillis();

        System.out.println("sum="+sum+"时间:"+(end-start)/2);

    }

    /*
        stream流计算
     */
    public static void test03(){
        Long start = System.currentTimeMillis();

        Long sum = LongStream.range(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);

        Long end  = System.currentTimeMillis();
        System.out.println("sum="+sum+"时间:"+(end-start));

    }
}

异步回调

Future 设计的初衷:对将来的某个事件结果进行建模!

package com.neihan.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/08/16:11
 * @Description:  CompletableFuture 线程异步回调
 */
public class TestFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        test02();

    }


    public void test01() throws ExecutionException, InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {

            //发起一个异步任务
            try {
                TimeUnit.SECONDS.sleep(2);

                System.out.println("这是一个异步的消息");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());

        });
        System.out.println("Main线程");
        System.out.println("future.get() = " + future.get());
    }


    /**
    * @Description: 有返回值的异步回调
    * @Param:
    * @return: Int
    * @Author: Neihan
    * @Date: 2021/11/8
    */
    public static void test02() throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {

            //发起一个异步任务
            try {
                TimeUnit.SECONDS.sleep(2);

                System.out.println("这是一个异步的消息");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int i = 10 /0;

            System.out.println(Thread.currentThread().getName());

            return 1024;   // 成功的返回值
        });


        System.out.println(future.whenComplete((t, u) -> {

            System.out.println(t);    // t 是正常返回的结果
            System.out.println(u);    // u 是错误的信息

        }).exceptionally((e -> {
            System.out.println("e.getMessage() = " + e.getMessage());
            return 500;   //返回error 的信息
        })).get());

    }
}

但是我们平时都使用CompletableFuture

JMM

什么是JMM?

JMM:JAVA内存模型,不存在的东西,是一个概念,也是一个约定!

关于JMM的一些同步的约定:

1、线程解锁前,必须把共享变量立刻刷回主存;

2、线程加锁前,必须读取主存中的最新值到工作内存中;

3、加锁和解锁是同一把锁;

线程中分为 工作内存、主内存

8种操作:

  • Read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用; - load(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中;

  • Use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令;

  • assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中; -

  • store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用; - write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中; -

  • lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态;

  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;

image-20200812215247240 image-20200812215606080
  • JMM对这8种操作给了相应的规定*:
  • - 不允许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 的理解

Volatile是 Java 提供的 轻量级 的同步机制

1、保证可见性

2、不保证原子性

3、禁止指令重排机制

保证可见性

volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据 一直死循环

package com.neihan.jmm;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/9:34
 * @Description: volatile
 *
 * 保证可见性
 * 不保证原子性
 * 禁止指令重排
 */
public class JMMDemo01 {

    private static  volatile int sum =1;    //volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{

            while (sum==1){

            }

        }).start();

        TimeUnit.SECONDS.sleep(1);

        sum=0;

        System.out.println("main");
    }
}

不保证原子性

如果不加lock和synchronized ,怎么样保证原子性?

使用原子类

这些类的底层都直接和操作系统挂钩!是在内存中修改值。

package com.neihan.jmm;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/9:41
 * @Description: 如果不加lock和synchronized ,怎么样保证原子性?
 * 使用原子类
 */
public class VDemo {

    private static  volatile  AtomicInteger count = new AtomicInteger();  
    
    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()+""+"count="+count);
    }


    public static void add(){
      /*  count++;   // ++ 不是原子性操作  是两个~3个操作*/

        count.incrementAndGet();
    }

}

禁止指令重排

什么是指令重排?

我们写的程序,计算机并不是按照我们自己写的那样去执行的

源代码–>编译器优化重排–>指令并行也可能会重排–>内存系统也会重排–>执行

int x=1; //1
int y=2; //2
x=x+5;   //3
y=x*x;   //4

//我们期望的执行顺序是 1_2_3_4  可能执行的顺序会变成2134 1324
//可不可能是 4123? 不可能的
1234567

可能造成的影响结果:前提:a b x y这四个值 默认都是0

|线程A|线程B
|------
|x=a|y=b
|b=1|a=2

正常的结果: x = 0; y =0;

|线程A|线程B
|------
|b=1|a=2
|x=a|y=b

可能在线程A中会出现,先执行b=1,然后再执行x=a;

在B线程中可能会出现,先执行a=2,然后执行y=b;

那么就有可能结果如下:x=2; y=1.

volatile可以避免指令重排:

volatile中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。

内存屏障:CPU指令。作用:

1、保证特定的操作的执行顺序;

2、可以保证某些变量的内存可见性(利用这些特性,就可以保证volatile实现的可见性)

image-20200812220019582

总结

  • volatile可以保证可见性;- 不能保证原子性- 由于内存屏障,可以保证避免指令重排的现象产生
    面试官:那么你知道在哪里用这个内存屏障用得最多呢?单例模式

玩转单例模式

饿汉式、DCL懒汉式

饿汉模式

单例模式私有构造

饿汉顾名思义 很饿 什么东西都先创建好

package com.neihan.jmm;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/10:26
 * @Description: 饿汉单例模式
 *
 * 单例模式全部是构造私有
 * 饿汉顾名思义 很饿 什么东西都先创建好
 *
 */
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 Hungry(){

    }

    private final static Hungry hungry = new Hungry();

    private static Hungry getInstance(){

        return hungry;
        
    }
}

DCL懒汉式

package com.neihan.jmm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/10:29
 * @Description: 懒汉单例模式
 * 很懒,所有东西不先创建
 */
public class LazyMan {


    private static boolean neihan = false;   //红绿灯

    private LazyMan(){
        synchronized (LazyMan.class){

            if(neihan == false){
                neihan=true;
            }else{
                throw new RuntimeException("小伙子不要试图使用反射来搞破坏!!!");
            }

        }

        System.out.println(Thread.currentThread().getName()+"ok");
    }

    private volatile static LazyMan lazyMan;

    private static LazyMan getInstance(){
        if (lazyMan ==null){


        synchronized (LazyMan.class){

            if(lazyMan==null){   //单线程下是OK 的  但是多线程是不行的  需要加锁  加锁后java中有反射!!!!
                lazyMan = new LazyMan();
            }
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) throws Exception {

     /*   for (int i = 0; i < 10; i++) {

            new Thread(()->{
                getInstance();
            }).start();
        }
*/


        //反射
        //LazyMan instance1 = LazyMan.getInstance();

        Field neihan = LazyMan.class.getDeclaredField("neihan");



        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);   //这个时候无视了私有的构造器,使用反射创建对象
        LazyMan instance2 = declaredConstructor.newInstance();

        neihan.setAccessible(true);
        neihan.set(instance2,false);   //把字段的值改变又可以了
        LazyMan instance1 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

静态内部类

package com.neihan.jmm;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/11:06
 * @Description:  静态内部类
 */
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.neihan.jmm;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/11:09
 * @Description: 枚举本身也是一个类
 * 在源码中看到Enum 是有无参构造的,原本的异常期望的是Cannot reflectively create enum objects
 *
 * 但是报的错误是 没有这个构造  java.lang.NoSuchMethodException: com.neihan.jmm.EnumSingle.<init>()
 * 通过反编译jad工具看到是 有个String 跟int 的构造,IDEA 骗了我们 给上String.class 跟int.class 之后我们发现到达了预期的报错
 * Cannot reflectively create enum objects
 * 使用枚举,我们就可以防止反射破坏了。
 *
 */
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }

}

class Test{

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        EnumSingle instance1 = EnumSingle.INSTANCE;

        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);

        EnumSingle enumSingle = declaredConstructor.newInstance();
        System.out.println(instance1);

        System.out.println(enumSingle);

    }
}

使用枚举,我们就可以防止反射破坏了。

image-20200812220204965

枚举类型的最终反编译源码:

public final class EnumSingle extends Enum
{<!-- -->
    public static EnumSingle[] values()
    {<!-- -->
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {<!-- -->
        return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {<!-- -->
        super(s, i);
    }

    public EnumSingle getInstance()
    {<!-- -->
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {<!-- -->
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {<!-- -->
            INSTANCE
        });
    }
}

深入理解CAS

什么是CAS?

大厂必须深入研究底层!!!!修内功!操作系统、计算机网络原理、组成原理、数据结构

意思就是比较并交换

package com.neihan.cas;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/14:02
 * @Description: CAS
 * 意思就是比较并交换
 * 当期望的值跟实际的值相同的时候就进行更新
 * Java 层面无法操作内存,java可以调用c++ native c++ 可以操作内存,java留了个后门 可以 通过 Unsafe 类操作
 *
 */
public class CASDemo {

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(1024);

        //public final boolean compareAndSet(int expect, int update)
        /**
        * @Description: CAS
        * @Param: 期望值、更新值
        * @return: boolean
        * @Author: Neihan
        * @Date: 2021/11/9
        */
        System.out.println(atomicInteger.compareAndSet(1024, 241));
        System.out.println(atomicInteger.get());

    }
}

Unsafe 类

image-20200812220347822 image-20200812220411463

总结

CAS:比较当前工作内存中的值 和 主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环,使用的是自旋锁。

缺点:

  • 循环会耗时;- 一次性只能保证一个共享变量的原子性;- 它会存在ABA问题

CAS : ABA 问题 (狸猫换太子)

image-20200812220441615

线程1:期望值是1,要变成2;

线程2:两个操作:

  • 1、期望值是1,变成3- 2、期望是3,变成1
    所以对于线程1来说,A的值还是1,所以就出现了问题,骗过了线程1;
public class casDemo {<!-- -->
    //CAS : compareAndSet 比较并交换
    public static void main(String[] args) {<!-- -->
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        //boolean compareAndSet(int expect, int update)
        //期望值、更新值
        //如果实际值 和 我的期望值相同,那么就更新
        //如果实际值 和 我的期望值不同,那么就不更新
        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());

        //因为期望值是2020  实际值却变成了2021  所以会修改失败
        //CAS 是CPU的并发原语
		//atomicInteger.getAndIncrement(); //++操作
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

原子引用

解决ABA问题,对应的思想:就是使用了乐观锁~

带版本号的 原子操作!

使用包装类会有对象引用的问题

Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。

image-20200812220608094

带版本号的原子操作

package com.neihan.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/14:30
 * @Description: ABA 问题  狸猫换太子  理论像乐观锁
 *
 *
 */
public class ABADemo {


    public static void main(String[] args) {

        //使用包装类会有对象引用的问题
        //Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(1,1);

        // CAS 比较交换
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();     //获得版本号
            System.out.println(Thread.currentThread().getName()+"Stamp=>"+stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            /**
            * @Description: CAS 比较 修改
            * @Param: 期望的值  修改的值  版本号  修改之后的版本号
            * @return: Boolean
            * @Author: Neihan
            * @Date: 2021/11/9
            */
            System.out.println(atomicStampedReference.compareAndSet(
                    1,
                    2,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1));

            System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());

            //修改完之后再修改回来,另一条线程并不知情,看看能不能修改成功
            System.out.println(atomicStampedReference.compareAndSet(
                    2,
                    1,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1));


            System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());


        },"A").start();


        new Thread(()->{

            int stamp = atomicStampedReference.getStamp();

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"Stamp"+stamp);

            System.out.println(Thread.currentThread().getName()+" "+atomicStampedReference.compareAndSet(1, 6, stamp, atomicStampedReference.getStamp() + 1));

            System.out.println(Thread.currentThread().getName()+"Stamp"+atomicStampedReference.getStamp());

        },"B").start();
    }
}

各种锁的理解

公平锁,非公平锁

1、公平锁:非常公平,不能插队,必须先来后到

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {<!-- -->
    sync = new NonfairSync();
}

2、非公平锁:非常不公平,允许插队,可以改变顺序

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {<!-- -->
    sync = fair ? new FairSync() : new NonfairSync();
}

可重入锁

image-20200812213957137

1、Synchonized 锁

synchronized 版本的可重入锁 自动获得内部锁

package com.neihan.lock;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/15:04
 * @Description:  synchronized 版本的可重入锁  自动获得内部锁
 */
public class Demo01 {

    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版本

Lock 版本的重入锁 Lock 锁一定是配对出现,不然就会出现死锁

package com.neihan.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/15:08
 * @Description: Lock 版本的重入锁   Lock 锁一定是配对出现,不然就会出现死锁
 */
public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone2 = new Phone2();

        new Thread(()->{
            phone2.sms();

        },"A").start();

        new Thread(()->{
            phone2.sms();

        },"B").start();
    }


}
class Phone2{


    Lock lock = new ReentrantLock();
    public  void sms(){

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"==>sms");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        call();   //这里也是一把锁
    }


    public  void call(){

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"==>call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

自旋锁

1、spinlock

public final int getAndAddInt(Object var1, long var2, int var4) {<!-- -->
    int var5;
    do {<!-- -->
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}

2、自定义自旋锁

基于CAS 实现

package com.neihan.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/15:25
 * @Description:  自定义的自旋锁  基于CAS 实现
 */
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.neihan.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/15:30
 * @Description:
 */
public class TestSpinLock {
    public static void main(String[] args) {

        SpinlockDemo spinlockDemo = new SpinlockDemo();

        new Thread(()->{

            try {
                spinlockDemo.myLock();
                TimeUnit.SECONDS.sleep(10);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"A").start();


        new Thread(()->{


            try {
                spinlockDemo.myLock();

                TimeUnit.SECONDS.sleep(2);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"B").start();

    }
}

死锁

image-20200812214548908
package com.neihan.lock;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/16:06
 * @Description: 死锁案例  以及排查
 * jps -l 查看进程
 * jstack 进进程号,进行排查
 *
 */
public class Deadlock {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"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);
            }
        }

    }
}

如何解开死锁

命令:jps -l

1、使用jps定位进程号,jdk的bin目录下: 有一个jps

image-20200812214833647

2、使用jstack 进程进程号 找到死锁信息

image-20200812214920583

一般情况信息在最后:

image-20200812214957930

while (!atomicReference.compareAndSet(null,thread)){

    }

}

public void myUnlock(){

    Thread thread = Thread.currentThread();
    System.out.println(thread.getName()+"===>myUnlock");

    atomicReference.compareAndSet(thread,null);

}

}


**测试类**

```java
package com.neihan.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/15:30
 * @Description:
 */
public class TestSpinLock {
    public static void main(String[] args) {

        SpinlockDemo spinlockDemo = new SpinlockDemo();

        new Thread(()->{

            try {
                spinlockDemo.myLock();
                TimeUnit.SECONDS.sleep(10);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"A").start();


        new Thread(()->{


            try {
                spinlockDemo.myLock();

                TimeUnit.SECONDS.sleep(2);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"B").start();

    }
}

死锁

image-20200812214548908
package com.neihan.lock;

import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: Neihan
 * @Date: 2021/11/09/16:06
 * @Description: 死锁案例  以及排查
 * jps -l 查看进程
 * jstack 进进程号,进行排查
 *
 */
public class Deadlock {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"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);
            }
        }

    }
}

如何解开死锁

命令:jps -l

1、使用jps定位进程号,jdk的bin目录下: 有一个jps

image-20200812214833647

2、使用jstack 进程进程号 找到死锁信息

image-20200812214920583

一般情况信息在最后:

image-20200812214957930
### 回答1: Java JUCJava Util Concurrent)是Java平台的一个并发编程库,提供了一些并发编程的工具和框架。以下是Java JUC的一些重要知识点: 1. Lock接口和ReentrantLock类:提供了一种比Java中的synchronized关键字更灵活、可定制化的同步机制。 2. Condition接口:可以和Lock接口一起使用,提供了一种等待通知机制,可以让线程在等待某个条件成立时挂起,直到被其他线程唤醒。 3. Semaphore类:提供了一种信号量机制,可以限制某些资源的并发访问量,保证程序的稳定性。 4. CountDownLatch类:提供了一种倒计时锁机制,可以让某个线程在其他线程都完成后再执行。 5. CyclicBarrier类:提供了一种栅栏机制,可以让多个线程在某个点上进行同步,等待所有线程都到达后再同时执行。 6. Executor框架:提供了一种线程池机制,可以更好地管理线程,提高程序的性能和稳定性。 7. CompletableFuture类:提供了一种异步编程机制,可以让程序在等待某些操作的同时继续执行其他操作,提高程序的并发性能。 这些都是Java JUC的重要知识点,掌握它们可以帮助开发者更好地编写高并发、高性能的程序。 ### 回答2: Java JUCJava Util Concurrency)是Java并发编程的工具类库,提供了一些多线程编程的辅助工具和数据结构,主要包括锁、原子变量、并发容器、线程池等。 首先,Java JUC提供了多种类型的锁,如ReentrantLock、ReadWriteLock等。这些锁可以用来控制对共享资源的访问,保证线程的安全性。通过使用锁,可以实现线程的互斥访问和公平竞争访问,防止资源的并发访问导致的数据不一致的问题。 另外,Java JUC还提供了一些原子变量,比如AtomicInteger、AtomicLong等。原子变量是线程安全的,可以保证对其操作的原子性。通过使用原子变量,可以避免多线程环境下对共享变量的竞争导致的数据错乱问题。 并发容器也是Java JUC的重要组成部分,如ConcurrentHashMap、ConcurrentLinkedQueue等。这些并发容器是线程安全的,可以在多线程环境下安全地处理数据。通过使用并发容器,可以提高多线程程序的性能和并发访问的效率。 最后,Java JUC还提供了线程池的支持,通过线程池可以实现线程的复用、统一管理和调度。线程池可以减少线程的创建和销毁的开销,并且可以控制并发线程的数量,避免因为线程数过多导致系统资源耗尽的问题。 总之,Java JUC知识点涵盖了锁、原子变量、并发容器和线程池等多个方面,可以帮助程序员更好地进行多线程编程,提高程序的性能和并发访问的效率。 ### 回答3: Java JUCjava.util.concurrent)是Java中用于处理多线程并发编程的工具包。它提供了一套强大的并发编程工具和类,帮助开发者更加方便地编写高效、稳定的多线程程序。 Java JUC包含了以下几个重要的知识点: 1. 锁机制:Java JUC提供了多种类型的锁机制,包括ReentrantLock、StampedLock等,用于实现线程同步和互斥访问共享资源。通过使用锁机制,可以确保多个线程之间的数据一致性和线程安全性。 2. 阻塞队列:Java JUC提供了多种类型的阻塞队列,如ArrayBlockingQueue、LinkedBlockingQueue等。阻塞队列是一种特殊的队列,当队列为空或者已满时,插入和删除操作会被阻塞,直到满足条件后再继续执行。 3. 线程池:Java JUC中的线程池机制可以重用线程,减少线程的创建和销毁开销,提高系统的性能和资源利用率。通过ThreadPoolExecutor类,可以方便地创建和管理线程池,并根据实际需求调整线程池的大小和线程池中线程的执行方式。 4. 原子操作:Java JUC提供了一系列原子类,如AtomicInteger、AtomicLong等,用于支持对共享变量进行原子操作,以避免线程竞争和数据不一致的问题。原子类提供了一系列原子性的方法,保证了多线程环境下的安全访问。 5. 并发容器:Java JUC提供了一些线程安全的并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等,用于在多线程环境下安全地处理数据结构。这些并发容器支持高并发读写操作,提供更好的性能和可伸缩性。 总之,Java JUC提供了一组强大的并发编程工具和类,能够帮助开发者更好地处理多线程编程中的并发性和线程安全性问题。通过熟练掌握和应用这些知识点,可以编写出高效、稳定的多线程程序。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值