JUC并发编程

什么是JUC

java.util工具包,包,分类

业务:普通的线程代码 Thread

Runnable 没有返回值,效率相对比 Callable

2.线程和进程

线程,进程,如果不能用一句话说出来的技术,不扎实!

进程:一个程序,QQ.exe Music.exe 程序的集合

一个进程往往包含多个线程至少包含一个!

java,默认有几个线程?两个 mian,和gc

线程:开了一个进程Typora,写字,自动保存(线程负责的)

对于java而言:Thread,Runnble,Callable

java真的可以开启线程吗? 开启不了的(他是通过本地方法调用,调用了底层的c++,java无法操作硬件)

并发,并行

并发编程,并发,并行

并发(多个线程操作同一个资源)

  • cpu一核,模拟出多条线程,天下武功为快不破,快速交替

并行(多个人一起行走)

  • cpu多核,多个线程可以同时执行(线程池)
package com;

public class Test078 {
    public static void main(String[] args) {
        //获取cup的核数.
        //cpu 密集型  IO 密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

并发编程的本质:充分利用Cpu的资源

线程有几个状态

new 线程新生

Runnable 运行

Blocked阻塞

Waitng等待,死死的等

Timed Watiing //超时等待

Terminated//终止

wait和sleep的区别

1.来自不同的类

wait => Object

sleep=>Thread

关于锁的释放

wait会释放锁,sleep不会释放锁,抱着锁睡觉,不会释放

使用的范围是不同的

wait:必须在同步代码块的时候使用

sleep 可以再任何地方使用

是否要捕获异常

sleep 需要捕获异常

wait 不需要捕获异常

Lock锁(重点)

image-20211012150009468

image-20211012150036872

公平锁:顾名思义,十分公平,必须按照顺序执行

非公平锁:不公平,可以插队(默认)

package com;

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

public class Dome99{
    public static void main(String[] args) {
        //并发多个线程操作同一个资源,把资源丢进线程里
        //并发多个线程操作同一个资源,把资源丢进线程里
        Tick tick = new Tick();
        new Thread(()->{ for (int i = 0; i < 60; i++) tick.sale(); },"A").start();
        new Thread(()->{ for (int i = 0; i < 60; i++) tick.sale(); },"b").start();
        new Thread(()->{ for (int i = 0; i < 60; i++) tick.sale(); },"c").start();
    }

    }


/**
 * lock锁
 * 1.new ReentrantLock();  创建可重复锁
 * 2.try 加锁 lock.lock();
 * 3.finally
 */
class Tick1{
    Lock lock = new ReentrantLock();
    private int number = 50;
    //麦票的方法
    public  void sale(){
        lock.lock();//加锁

        try {
            //业务代码
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,还剩"+number+"张");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
}

Synchronized 和 Lock 区别

  1. Synchronized 内置的java关键字 lock是一个java类
  2. Synchronized 无法判断锁的状态, lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,lock必须要手动释放锁,如果不释放锁就是形成死锁
  4. Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等),loc就不一定会等待下去
  5. Synchronized 可重入锁,不可以中单的,非公平锁,lock,可重入锁,可以判断锁,非公平(可以自己设置);
  6. Synchronized 适合少量的代码同步问题,lock 适合锁大量的同步代码

面试的:单列模式,排序算法,生产者和消费者,死锁

生产者和消费者问题Synchronized 版本

package com.pc;

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"B").start();

    }
}
class Data{//数字资源类
    private int number = 0;

    public synchronized void increment(){//增值
        if (number != 0 ){
            //等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        //通知其他线程我-1完毕了
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+">"+number);
    }
    public synchronized void  decrement(){//减值
        if (number == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        //通知其他线程我-1完毕了
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+">"+number);
    }
}

问题存在 Abcd 4个线程,会存在虚假唤醒

image-20211013232849729

package com.pc;

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"c").start();



        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"d").start();
    }
}
class Data{//数字资源类
    private int number = 0;

    public synchronized void increment(){//增值
        while (number != 0 ){
            //等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        //通知其他线程我-1完毕了
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+">"+number);
    }
    public synchronized void  decrement(){//减值
        while (number == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        //通知其他线程我-1完毕了
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()+">"+number);
    }
}

将if改为While判断

因为线程唤醒后会从waic的地方继续执行,使用if的话,唤醒之后不会进行判断(因为wait之前已经判断过了)使用循环的话唤醒后悔继续判断

通过Lock找到Condition

image-20211015160035328

image-20211015160048347

代码实现:

package com.pc;

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

public class D{
    public static void main(String[] args) throws Exception{
        Data3 data = new Data3();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"c").start();



        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"d").start();

    }
}
class Data3{//数字资源类
    private int number = 0;
//     Lock lock = new ReentrantLock();//重新进入锁
//    Condition condition = lock.newCondition();
//        condition.await();//等待
//        condition.signalAll();//唤醒全部对象
    Lock lock = new ReentrantLock();//创建可重入锁
    Condition condition = lock.newCondition();//通过lock创建Condition对象,通过这个对象调用方法

    public  void increment()  {//增值
        lock.lock();
        while (number != 0 ){
            //等待
            try {
                condition.await();//让对象等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        //通知其他线程我-1完毕了
        condition.signalAll();//唤醒对象
        System.out.println(Thread.currentThread().getName()+">"+number);
    }
    public  void  decrement(){//减值
        lock.lock();
        while (number == 0){
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        //通知其他线程我-1完毕了
        condition.signalAll();
        System.out.println(Thread.currentThread().getName()+">"+number);
    }
}

任何一个新的技术,绝对不仅仅是覆盖了原来的技术,优势和补充

Condition 精准的通知和唤醒线程

代码实现

package com.pc;

import java.security.PublicKey;
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 Q {
    public static void main(String[] args) {
        Data5 date = new Data5();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                date.pintA();
            }
        },"A").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) {
            date.pintB();
        }},"B").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) {
            date.pintC();
        }},"C").start();
    }
}
class Data5{
    private Lock lock = new ReentrantLock();//创建可重新进入锁
    private Condition condition1 = lock.newCondition();//创建监视器

    private int number = 1;//创建标志位
    public void pintA(){
        lock.lock();
        //业务  判断  执行 通知
        try {
            while (number !=1){//如果number不等于1
                condition1.await();//condition1暂停
            }
            System.out.println(Thread.currentThread().getName()+"AAAAAAA");
            number = 2;
            condition1.signal();//唤醒单个线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }
    public void pintB(){
        lock.lock();
        try {
            while (number != 2){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"BBBBBBB");
            number = 3;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void pintC(){
        lock.lock();
        try {
            while (number != 3){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"CCCCC");
            condition1.signal();
            number = 1;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

什么是锁

8锁现象

如何判断锁的是谁!永远知道什么锁,锁到底锁的谁

对象,Class

package com.pc;

import java.util.concurrent.TimeUnit;

/*
*8锁 就是关于锁的八个问题
* 1.两条线程同时执行,哪个先打印? 先打印发短信,然后打电话
* 2.sendSms方法延迟4秒,那个先打印?先打印发短信,然后打电话
 */
public class Test01 {
    public static void main(String[] args) {

        Phone phone = new Phone();
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);//线程睡眠一秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{//电话

    /**
     * 1.synchronized 锁的对象是方法的调用者
     * 2.两个方法用同一个锁,谁先调用,谁先执行
     */
    public synchronized void sendSms(){//发送短信
        System.out.println("sendSms 发短信");
    }
    public synchronized void call(){//打电话
        System.out.println("call 打电话");
    }
}
package com.pq;
/**
 * 增加了一个普通方法后!先执行发短信还是Hello?先执行普通方法
 * 两个对象,两个同步方法,发短信还是打电话?//打电话
 */

import java.util.concurrent.TimeUnit;

public class Test01  {
    public static void main(String[] args) {

       Phone phone = new Phone();
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);//线程睡眠一秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}
class Phone{//电话

    /**
     * 1.synchronized 锁的对象是方法的调用者
     * 2.两个方法用同一个锁,谁先调用,谁先执行
     */
    public synchronized void sendSms(){//发送短信
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendSms 发短信");
    }

    public synchronized void call(){//打电话
        System.out.println("call 打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}
package com.pq;import java.util.concurrent.TimeUnit;/** * 5.增加两个静态同步的方法,只有一个对象,先打印发短信还是打电话? * 6.两个对象.增加两个静态同步的方法,只有一个对象,先打印发短信还是打电话? */public class Test03   {    public static void main(String[] args) {        //两个对象的类模板只有一个,static 锁的是class        Phone1 phone = new Phone1();        Phone1 phone1 = new Phone1();        new Thread(()->{            phone.sendSms();        },"A").start();        try {            TimeUnit.SECONDS.sleep(1);//线程睡眠一秒        } catch (InterruptedException e) {            e.printStackTrace();        }        new Thread(()->{            phone1.call();        },"B").start();    }}class Phone1{//电话    /**     * 1.synchronized 锁的对象是方法的调用者     * 2.两个方法用同一个锁,谁先调用,谁先执行     */    public static synchronized void sendSms(){//发送短信        try {            TimeUnit.SECONDS.sleep(3);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("sendSms 发短信");    }    public static synchronized void call(){//打电话        System.out.println("call 打电话");    }}
package com.pq;import java.util.concurrent.TimeUnit;/** * 1.一个静态的同步方法一个,普通的同步方法,一个对象,先打印哪个? * 2.一个静态的同步方法一个,普通的同步方法,两个个对象,先打印哪个? */public class Test04   {    public static void main(String[] args) {        //两个对象的类模板只有一个,static 锁的是class        Phone4 phone = new Phone4();        Phone4 phone1 = new Phone4();        new Thread(()->{            phone.sendSms();        },"A").start();        try {            TimeUnit.SECONDS.sleep(1);//线程睡眠一秒        } catch (InterruptedException e) {            e.printStackTrace();        }        new Thread(()->{            phone1.call();        },"B").start();    }}class Phone4{//电话    /**     * 1.synchronized 锁的对象是方法的调用者     * 2.两个方法用同一个锁,谁先调用,谁先执行     */    public static synchronized void sendSms(){//发送短信        try {            TimeUnit.SECONDS.sleep(3);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("sendSms 发短信");    }    public  synchronized void call(){//打电话        System.out.println("call 打电话");    }}

小结

new this 具体的一个手机

static Class 唯一的一个模板

集合不安全

package com.pc;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;//public class ListTest {    public static void main(String[] args) {        /**         * java.util.ConcurrentModificationException  并发修改异常         * 解决方案.         * 1.List<String> list = new Vector<>();         * 2.List<String> list = Collections.synchronizedList(new ArrayList<>());         * 3.List<String> list = new CopyOnWriteArrayList<>();         * CopyOnWrite写入时复制  COW 计算机程序设计领域的一种优化策略         * 多个线程调用的时候list  读取的时候,固定的,写入(覆盖)         * 在写入的时候避免覆盖,造成数据问题!         *         * 读写分离         */        List<String> list = new CopyOnWriteArrayList<>();        for (int i = 0; i < 10; i++) {            new Thread(()->{                list.add(UUID.randomUUID().toString().substring(0,5));                System.out.println(list);            },String.valueOf(i)).start();        }    }}

小狂神的学习方法推荐:1.先会用,2.货比三家,寻找更好的解决方法,3.看源码

HashSet异常

package com.pc;import java.awt.datatransfer.ClipboardOwner;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;/** * 同理可证 ConcurrentModificationException  并发修改异常 * 解决方法 * 1.Set<Object> set = Collections.synchronizedSet(new HashSet<>()); * 2. Set<Object> set = new CopyOnWriteArraySet<>(); */public class SetTest {    public static void main(String[] args) {        Set<Object> set = new CopyOnWriteArraySet<>();        for (int i = 0; i < 10; i++) {            new Thread(()->{                set.add(UUID.randomUUID().toString().substring(0,5));                System.out.println(set);            },String.valueOf(i)).start();        }    }}

hashSet底层是什么?

public HashSet() {    map = new HashMap<>();}//底层还是HashMap
package com.pc;import java.util.Collections;import java.util.HashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArrayList;public class MapTest {    public static void main(String[]args){        //解决方法 Map<String, String> map =  new ConcurrentHashMap<>();        Map<String, String> map =  new ConcurrentHashMap<>();        for (int i = 0; i < 10; i++) {            new Thread(()->{                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));                System.out.println(map);            },String.valueOf(i)).start();        }    }}

Callable(简单)

image-20211020002721284

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,run/call

代码测试

细节

  1. 有缓存
  2. 结果可能需要等待,会阻塞
package com.pc;

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 {
        MyCallable a = new MyCallable();
        FutureTask futureTask = new FutureTask(a);//创建适配类
        new Thread(futureTask,"s").start();
        new Thread(futureTask,"a").start();//会缓冲,优化代码
        Integer o = (Integer)futureTask.get();//查找返回值
        System.out.println(o);//有可能会阻塞


    }
}
class MyCallable implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("我是王刚");
        return null;
    }
}

Semaphore

semaphore:信号量

image-20211022135541894

package com.Add;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class semaphoreDome {    public static void main(String[] args) {        Semaphore semaphore = new Semaphore(3);        for (int i = 0; i < 6; i++) {            new Thread(()->{                try {                    semaphore.acquire();//取得线程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();//释放线程                }            }).start();        }    }}

原理:

Semaphore.acquire();//获得,假设已经满了,那就等待释放为止

Semaphore.release();//释放,会将当前的信号释放,唤醒等待的线程

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

读写锁

ReadWriteLock

image-20211022143903283

package com.Add;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * 独占锁(写锁) 一次只能被一个线程占有 * 共享锁(读锁) 多个线程可以同时占有 * ReadWriteLock * 读 - 读 可以共存 * 读 - 写 不能共存 * 写 - 写 不能共存 */public class ReadWriterLockDome {    public static void main(String[] args) {        MyCacheLock myCacheLock = new MyCacheLock();        for (int i = 0; i < 6; i++) {            final int temp = i;            new Thread(()->{                myCacheLock.put            },String.valueOf(i)).start();        }    }}class MyCacheLock{    private volatile Map<String,Object> map = new HashMap<>();    private ReadWriteLock Re = new ReentrantReadWriteLock();//读写锁    private void put(String key,Object vlue){        Re.writeLock().lock();//加锁        try {            System.out.println(Thread.currentThread().getName()+"写入"+key);            put(key,vlue);            System.out.println(Thread.currentThread().getName()+"写入成功");        } catch (Exception e) {            e.printStackTrace();        } finally {            Re.writeLock().unlock();//解锁        }    }    private void get(String key){        Re.readLock().lock();        try {            System.out.println(Thread.currentThread().getName()+"读取"+key);            Object o = map.get(key);            System.out.println(Thread.currentThread().getName()+"读取"+key);        } catch (Exception e) {            e.printStackTrace();        } finally {            Re.readLock().unlock();        }          }}

阻塞队列

image-20211022172621594

image-20211022172633518

BlockingQueue 不是新东西

image-20211022173600947

image-20211022173835505

什么情况下我们会使用阻塞队列:多线程并发处理,线程池

学会使用队列

添加,移除

四种Api

方式 抛出异常 有返回值 阻塞等待 超时等待
添加 add offer put offer(;😉
移除 remove poll take poll(,)
判断队列首 element peek --- ---
  1. 会抛出异常
  2. 不会抛出异常
  3. 阻塞等待
  4. 超时等待
package com.Add;import java.util.concurrent.ArrayBlockingQueue;public class Test {    public static void main(String[] args) {//队列的大小        ArrayBlockingQueue<Object> q = new ArrayBlockingQueue<>(3);        System.out.println(q.add(3));        System.out.println(q.add(4));        System.out.println(q.add(5));       //IllegalStateException        System.out.println(q.remove());        System.out.println(q.remove());        System.out.println(q.remove());          }}
package com.Add;import java.util.concurrent.ArrayBlockingQueue;public class Test {    public static void main(String[] args) {        ArrayBlockingQueue<Object> q = new ArrayBlockingQueue<>(3);        System.out.println(q.offer(3));        System.out.println(q.offer(4));        System.out.println(q.offer(5));        System.out.println(q.offer(5));//false不抛出异常       //IllegalStateException        System.out.println(q.poll());        System.out.println(q.poll());        System.out.println(q.poll());        System.out.println(q.poll());//null不抛出异常    }}
package com.Add;import java.sql.Time;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.TimeUnit;public class Test {    public static void main(String[] args) throws InterruptedException {        ArrayBlockingQueue<Object> q = new ArrayBlockingQueue<>(3);        //等待阻塞        q.offer(9);        q.offer(6);        q.offer(9);        q.offer(9, 1,TimeUnit.SECONDS);       //IllegalStateException        System.out.println("=======");        System.out.println(q.poll());        System.out.println(q.poll());        System.out.println(q.poll());        System.out.println(q.poll(2,TimeUnit.SECONDS));//没有这个元素一种阻塞    }}

SynchronousQueue同步队列

没有容量

进去一个元素,必须等待取出来之后,才能再往里面放一个元素

put,take

学科package com.Add;import java.util.concurrent.SynchronousQueue;import java.util.concurrent.TimeUnit;/** * 同步队列 * 和其他的BlockingQueue不一样,SynchronosuQueue不存储元素 *put了一个元素,必须从里面先take取出来,否则不能在put进去值! */public class SynchronousQueueDemo {    public static void main(String[] args) {        SynchronousQueue<Object> a = new SynchronousQueue<>();        new Thread(()->{            try {                System.out.println(Thread.currentThread().getName()+"Th01");                a.put("1");                System.out.println(Thread.currentThread().getName()+"Th02");                a.put("2");                System.out.println(Thread.currentThread().getName()+"Th03");                a.put("6");            } catch (InterruptedException e) {                e.printStackTrace();            }        },"T1").start();        new Thread(()->{            try {                TimeUnit.SECONDS.sleep(3);                System.out.println(Thread.currentThread().getName()+"=="+a.take());                TimeUnit.SECONDS.sleep(3);                System.out.println(Thread.currentThread().getName()+"=="+a.take());                TimeUnit.SECONDS.sleep(3);                System.out.println(Thread.currentThread().getName()+"=="+a.take());            } catch (InterruptedException e) {                e.printStackTrace();            }        },"T2").start();    }}

学了技术不会用,看的少

11.线程池(重点)

线程池:三大方法,七大参数,四种拒接策略

池化技术

程序的运行,本质上就是会占用系统的资源,可以通过池化技术来,优化资源的使用

线程池,连接池,内存池,对象池///创建和销毁,都是十分的浪费资源

池化技术:事先准备好一些资源,有人要用,就来我这里那,用完之后还给我

线程池的好处:

  1. 降低资源的消耗
  2. 提高响应速度
  3. 方便管理

线程复用,可以控制最大并发数,管理线程

线程三大方法

package com.Pool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//Executor三大方法,工具类public class Dome01 {    public static void main(String[] args) {       // ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单一线程池        // ExecutorService threadPool =Executors.newFixedThreadPool(6);//创建固定线程池子        ExecutorService threadPool =Executors.newCachedThreadPool();//创建智能线程池(根据线程的多少来指定线程的数量)        try {            for (int i = 0; i < 100; i++) {                //创建了线程池就要用线程池去调用方法,不要用new Thread().start();了                threadPool.execute(()->{                    System.out.println(Thread.currentThread().getName()+"ok");                });//execute是执行的意思            }        } catch (Exception e) {            e.printStackTrace();        } finally {            threadPool.shutdown();//线程使用完成必须关闭线程池        }    }}

七大参数

源码分析

public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue<Runnable>());}public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());                                     // 本质;ThreadpoolExecutor()                                      public ThreadPoolExecutor(int   corePoolSize,//核心线程池大小                              int maximumPoolSize,//最大线程池大小                              long keepAliveTime,//超时释放                              TimeUnit unit,//超时单位                              BlockingQueue<Runnable> workQueue,//阻塞队列                              ThreadFactory threadFactory,//线程工程                              RejectedExecutionHandler handler//拒绝策略                              ) {        if (corePoolSize < 0 ||            maximumPoolSize <= 0 ||            maximumPoolSize < corePoolSize ||            keepAliveTime < 0)            throw new IllegalArgumentException();        if (workQueue == null || threadFactory == null || handler == null)            throw new NullPointerException();        this.acc = System.getSecurityManager() == null ?                null :                AccessController.getContext();        this.corePoolSize = corePoolSize;        this.maximumPoolSize = maximumPoolSize;        this.workQueue = workQueue;        this.keepAliveTime = unit.toNanos(keepAliveTime);        this.threadFactory = threadFactory;        this.handler = handler;

image-20211024220640982

image-20211024221434118

手动创建线程池

package com.Pool;import java.util.concurrent.*;//拒绝策略new ThreadPoolExecutor.CallerRunsPolicy() //哪里来的灰哪里// new ThreadPoolExecutor.AbortPolicy() //抛异常//   new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了会和最老的竞争,也不会抛出异常// new ThreadPoolExecutor.DiscardPolicy() //队列满了不会抛出异常,丢掉数据public class Dome01 {    public static void main(String[] args) {        //自定义线程池,new ThreadPoolExecutor        Executors.newSingleThreadExecutor();        ExecutorService threadPool =new ThreadPoolExecutor(2,//核心线程数              5,//最大线程数              3, TimeUnit.SECONDS,//超时等待的事件                new LinkedBlockingDeque<>(3),//线程队列                Executors.defaultThreadFactory(),//线程工程              new ThreadPoolExecutor.DiscardPolicy() //拒绝策略              );//创建单一线程池        try {            //最大值,就是队列+max的值            for (int i = 1; i <= 1000000; i++) {                //创建了线程池就要用线程池去调用方法,不要用new Thread().start();了                threadPool.execute(()->{                    System.out.println(Thread.currentThread().getName()+"ok");                });//execute是执行的意思            }        } catch (Exception e) {            e.printStackTrace();        } finally {            threadPool.shutdown();//线程使用完成必须关闭线程池        }    }}

四种拒绝策略

image-20211024222103619

拒绝策略

//拒绝策略new ThreadPoolExecutor.CallerRunsPolicy() //哪里来的灰哪里// new ThreadPoolExecutor.AbortPolicy() //抛异常//   new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了会和最老的竞争,也不会抛出异常// new ThreadPoolExecutor.DiscardPolicy() //队列满了不会抛出异常,丢掉数据

小结和扩展

池的大小该如何去设置

了解IO密集型,cpU密集型(调优的)

  1. 最大线程到底该如何定义
  2. CPU 密集型,几核,就是几,可以保证cpu的效率最高
  3. IO 密集型 > 判断你程序中十分耗IO的线程

四大函数式接口(必须掌握)

新时代的程序员必须会:lambda,链式编程,函数式接口,Stream流式计算

函数式接口:只有一个方法的接口

@FunctionalInterfacepublic interface Runnable {    /**     * When an object implementing interface <code>Runnable</code> is used     * to create a thread, starting the thread causes the object's     * <code>run</code> method to be called in that separately executing     * thread.     * <p>     * The general contract of the method <code>run</code> is that it may     * take any action whatsoever.     *     * @see     java.lang.Thread#run()     */    public abstract void run();    //超级多FunctionalInterface    //简化编程满血,在新版本的框架最大量引用    //foreach(消费者的函数式接口)

image-20211026104304924

代码测试

Function函数式接口

image-20211026144611679

package com.Pool;import java.util.concurrent.*;/** * Function 函数型接口,有个输入参数,有一个输出 * 只要是函数式接口就可以用lambad表达式来进行简化 */import java.util.function.Function;public class Dome02 {    public static void main(String[] args) {        Function<String,Object> function = (a)->{            return a;        };        System.out.println(function.apply("3"));    }}

段定形接口

package com.Pool;import java.util.concurrent.*;import java.util.function.Function;import java.util.function.Predicate;public class Dome02 {    public static void main(String[] args) {        Predicate<String> predicate = (str)->str.isEmpty();        System.out.println(predicate.equals(0));    }}

Consumer 消费型接口

package com.Pool;import java.util.function.Consumer;public class Dome02 {    /**     * Consumer 消费型接口,只有输入没有返回值     *     */    public static void main(String[] args) {        Consumer<String> consumer = (str)->{            System.out.println(str);        };        consumer.accept("aaa");    }}

Supplier供给型接口

image-20211026200337423

package com.Pool;import java.util.function.Consumer;import java.util.function.Supplier;public class Dome02 {    /**     * Consumer 消费型接口,只有输入没有返回值     *     */    public static void main(String[] args) {        Supplier supplier = ()-> 1024;        System.out.println(supplier.get());    }}

Stream流式计算

什么是流式计算

大数据= 储存+计算

集合 , MySQL本质就是存储东西的;

计算都应该交给流来操作

image-20211026221903698

package com.Pool;
import java.sql.Time;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class Dome02 {
    /**
     *1.年龄必须是偶数
     * 2.年龄必须大于23岁
     * 3.用户名转换为大写字母
     * 4.用户名字母倒着排序
     * 5.只能输出一个用户
     *
     */
    public static void main(String[] args) {

        User u1 = new User(1,"a",21);
        User u2= new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5= new User(5,"e",25);
        User u6 = new User(6,"f",26);
        //集合就是储存
        List<User> list =Arrays.asList(u1,u2,u3,u4,u5,u6);
        //
        list.stream()
                .filter(u->{return u.getID()%2 ==0;})//选择偶数
                .filter(u->{return u.getAge() > 23;})//选择23以上的岁数
                .map(u -> u.getName().toUpperCase() )//打印大写名字
                .sorted((uu1,uu2)->uu2.compareTo(uu1))//倒着输出用户
                .limit(1)//限制一个输出
                .forEach(System.out::println);//打印



    }
}

ForkJoin

什么是ForkJoin

ForkJoin在jdk1.7,并行执行任务!提高效率,大数据量;

大数据:Map Reduce(把大任务拆分成小任务)

FoekJoin特点:工作窃取

这个里面维护的都是双端队列

image-20211026234525568

package com.Add;

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

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

        //dome01();//10秒
        dome03();//
    }
    public static void dome01(){
        Long sum =0L;
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            sum+=i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
    public static void dome02() throws ExecutionException, InterruptedException {//会使用forJoin

        long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkloinDemo(0L, 1000000000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    } public static void dome03(){

        long start = System.currentTimeMillis();
        Long sum = LongStream.rangeClosed(0L,1000000000L).parallel().reduce(0,Long::sum);//使用并行流
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }


}

测试

package com.Add;import java.util.concurrent.RecursiveTask;/** * 求和计算的任务 * 3000  6000(Forkjoin) 9000(stream并行流) * 如何使用forkJoin * 计算类要继承 ForkjoinTask */public class ForkloinDemo extends RecursiveTask<Long> {    private  Long start;    private Long end;    //临界值    private Long temp = 10000L;    public ForkloinDemo() {    }    public ForkloinDemo(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+=1;            }            return sum;        }else {//forkjoim方法实现            long middle = (end+start)/2;//中间值            ForkloinDemo task1 = new ForkloinDemo(start, middle);            task1.fork();//拆分任务吧任务压入线程            ForkloinDemo task2 = new ForkloinDemo(middle+1,end );            task2.fork();//拆分任务把任务压入线程            return task1.join()+task2.join();        }    }}

异步回调

Futur 设计的初衷:对将来的某个事物的结果进行建模

image-20211027201148284

package com.Add;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;/** * 异步调用:completableFuture * 异步执行 * 成功回调 * 失败回调 */public class Dome09 {    //没有返回值的异步回调    public static void main(String[] args) throws ExecutionException, InterruptedException {//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{//            try {//                TimeUnit.SECONDS.sleep(2);////            } catch (InterruptedException e) {//                e.printStackTrace();//            }//            System.out.println(Thread.currentThread().getName()+"runAsgnc");//        });//        System.out.println("1111");//        completableFuture.get();//获得阻塞        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{            int a = 10/0;            System.out.println(Thread.currentThread().getName()+"Integer");            return 1024;        });        System.out.println(completableFuture.whenComplete((t,u)->{            System.out.println("t="+t);//先存入的数是1024            System.out.println("u="+u);//错误信息java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero        }).exceptionally((e)->{            System.out.println(e.getMessage());            return 233;//可以获取到错误的返回结果        }).get());    }}

Volatile

请你谈谈你对Volatile的理解

Volatile是java虚拟机提供轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么是JMM

JMM:java内存模型,不存在的东西,是一个概念,事宜约定

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

  1. 线程解锁前,必须吧共享变量立刻刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存中
  3. 加锁和解锁是同一把锁

8种操作

image-20211027222737111

MM(Java内存模型)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。总结:就是规范,抽象的模型。
*
1)内存:共享的数据
2)工作空间:工作内存,基本数据类型,直接分配到工作内存,引用类型的数据将引用的地址存放在工作内存,引用的对象存放堆中。
3)工作方式:
A 线程修改私用数据,直接在工作空间修改。
B 线程修改共享数据,把数据复制到工作空间中去,在工作空间修改,修改完毕以后,再刷新内存中的数据。
2.JMM用来干什么?

JMM是Java内存模型,既然是模型,必定是能提供一种规范指导产出一种产品的东西。那么JMM指导的是什么呢?JVM就是根据JMM在物理内存划分出的结果。Java内存模型的作用是规范内存数据和工作空间数据的交互 。
3.计算机与JMM

先看看计算机的硬件内存架构:
*
计算机硬件架构存在的问题:
多个CPU并行处理缓存数据时,并发处理的不同步,会产生数据一致性问题。
解决方案:
Ⅰ. 总线加锁:CPU核执行一个线程去访问数据做操作的时候,向总线上发送一个LOCK信号,通过把内存和CPU之间的通信锁住,把并行化的操作变成了串行。虽然能解决一致性问题,但是会导致很严重的性能问题。
Ⅱ. 缓存的一致性协议(MESI):当CPU在Cache中操作数据时,如果该数据是共享变量,数据在Cache读到寄存器中,进行新修改,并更新内存数据Cache Line置无效。其他的CPU拿取数据,若Cache Line为无效会从内存中拿取。

JMM与计算机的硬件内存架构非常的相似。所以会遇到相似的问题和对应问题的解决方案。并发会有三个重要的特征,Java对这些特征有对应的保证方式:

1、原子性:不可分割,像变量的赋值x=1便是原子性,但y=x没有原子性,因为先是从x读到工作空间(原子性),把x的值写到y(原子性)。同理x++,z=z+1都没有原子性。多个原子性的操作合并没有原子性。(Java的保证操作:Synchronized 、JUC Lock的lock)
2、可见性:线程只能操作自己工作空间中的数据(Java的保证操作:Volatile、Synchronized、JUC Lock的lock)
3、有序性:程序中的顺序不一定就是执行的顺序,因为涉及编译时编译重排序、指令重排序来提高效率(Java的保证操作: Volatile、Synchronized)

问题:程序中的值已经不知道主内存中的值被修改过了

image-20211027223454539

Volatile

保证可见性

package com.Add;import java.util.concurrent.TimeUnit;public class TestJMM {    /**     * 不加 volatile会死循环     * 加上volatile可以保证可见性     */    private volatile static int num = 0;    public static void main(String[] args) throws InterruptedException {        new Thread(()->{            while(num==0){//线程一是不知道程序的变化的            }        }).start();        TimeUnit.SECONDS.sleep(2);       num = 2;        System.out.println(num);    }}
package com.Add;public class VDome {    //Volatilc不保证原子性    private volatile static int num = 0;    private  static void add(){        num++;    }    public  static void main(String[] args) {        for (int i = 1; i <= 20; i++) {            new Thread(()->{                for (int i1 = 0; i1 < 1000; i1++) {                    add();                }            }).start();        }        while (Thread.activeCount()>2){//如果线程活跃数量超过2            Thread.yield();//礼让线程        }        System.out.println(Thread.currentThread().getName()+" "+num);    }}

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

image-20211027230458889

image-20211027230604741

使用原子类解决,原子性问题

package com.Add;

import java.util.concurrent.atomic.AtomicInteger;

public class VDome {
    //Volatilc不保证原子性
    //可以用AtomicInteger
    private volatile static AtomicInteger num = new AtomicInteger(1);
    private  static void add(){
        num.getAndIncrement();//num+1方法  ,gas
    }

    public  static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){//如果线程活跃数量超过2

            Thread.yield();//礼让线程
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

这些类的底层都直接和操作系统挂钩!在内存中修改值!,unsafe类是一个很特殊的存在

指令重排

什么是指令重排:你写的程序, 计算机并不是按照你写的那样去执行的.

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

处理器在进行指令重排的时候,考虑:数据之间的依赖性

指令重排可能造成影响的家国:abxy这四个默认值都是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

指令重排导致的诡异结果:x=2;y=1;

Volatile可以避免指令重排

内存屏障,cpu指令.作用:

  1. 保证特点的操作的执行顺序
  2. 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)

image-20211027233553852

Volatile 是保持可见性,不保证原子性,可以避免指令重排的现象产生

彻底玩耍单列模式

饿汉式,DCL懒汉式,深究!

posted @ 2021-10-28 20:38  πππ·  阅读(111)  评论(0)    收藏  举报