1.什么是JUC

java 怎么并发调用别人接口_System

java.util工具包、包、分类

Runnable 没有返回值、效率相比于Callable相对较低

2.线程和进程

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

java 怎么并发调用别人接口_java 怎么并发调用别人接口_02

一个进程往往可以包含多个线程,至少包括一个!

java默认有两个线程。main GC

native

native是一个计算机函数,一个Native Method就是一个Java调用非Java代码的接口。方法的实现由非Java语言实现,比如C或C++。

**java真的可以开启线程吗?**不可以的

new thread().start();start方法底层调用的是一个本地方法(native),调用的底层的C++,java无法操作硬件。所以java不可以开启线程

java 怎么并发调用别人接口_java 怎么并发调用别人接口_03

并行,并发

并发(多个线程操作一个资源)交替的

  • CPU在一核的情况下,模拟出来多条线程,解决方法,天下武功,唯快不破,快速交替

并行(多个人一起走)同时的

  • CPU多核,多个线程可以同时执行;线程池

查看cpu的核数的三种方式:

**第一种:**此电脑–>管理–>设备管理器

java 怎么并发调用别人接口_i++_04

**第二种:**任务管理器–>CPU—>逻辑处理器

java 怎么并发调用别人接口_System_05

**第三种:**通过java

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

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

线程有几个状态

通过源码分析:线程有六个状态NEW /RUNNABLE/BLOCKED/WAITING/TIMED_WAITING/TERMINATED

public enum State {
	//新生
        NEW,
	//运行
        RUNNABLE,
	//阻塞
        BLOCKED, 
    //死死的等
        WAITING,
    //超时等待
        TIMED_WAITING,
      //阻止
        TERMINATED;
    }

wait/sleep区别

  1. 来自不同的类
  • wait—>Object
  • sleep—>Thread
  1. 关于锁的释放
  • wait会释放锁
  • sleep不会释放锁抱着锁睡觉
  1. 使用的范围不同
  • wait必须在同步代码块中使用
  • sleep可以在任何的地方使用
  1. 是否需要捕获异常
  • wait不需要捕获异常
  • sleep需要捕获异常

3.Lock锁(重点)

传统的synchronized

通过传统的synchronized锁来解决卖票的问题

/*
* 真正的多线程开发,公司中的开发 降低耦合性。
*线程就是一个单独的资源类,没有任何附属的操作
* 1.属性
* 2.方法
*/
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        //并发:多个线程操作同一个资源类
        Ticket ticket =new Ticket();
        //@FunctionalInterface  函数式接口, jdk1.8之后  lambda表达式 (参数)->{代码块}
        //通过资源来操作 ticket.sale()来调用操作买票  lambda表达式实现的是Runnable接口
        //并发三个线程操作同一个资源,ticket类就解耦,不需要实现runnable接口
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }},"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }},"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }},"C").start();
    }
}
//一个标准的资源类 OOP原则
 class  Ticket{
    //属性、方法
    private  int num =50;
    //卖票的方式
    //使用传统的synchronized
    //锁:锁的是对象和class
     public  synchronized  void sale(){
        //如果num>0就可以卖
        if(num>0){
            System.out.println(Thread.currentThread().getName()+"卖出了"+num-- +"票"
            +"剩余了"+num);
        }
    }
}

Lock接口

java 怎么并发调用别人接口_System_06

lock接口的三个实现类:

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

进入ReentrantLock锁:

RenntrantLock锁有两个构造:

一个无参构造:它是一把非公平锁

一个是一个boolean值参数的锁:如果为true则为公平锁,如果为false则为非公平锁

java 怎么并发调用别人接口_java_07

公平锁与非公平锁的区别:

公平锁:十分的公平,可以先来后到

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

使用lock的三步使用

//第一步创建锁
Lock lock= new ReentrantLock();
//第二步打开锁
lock.lock();
try{  
    //业务代码
}

finally{
    //第三步关锁
    lock.unlock();
    
}

使用lock锁完成卖票的代码:

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

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        Ticket02 ticket02 = new Ticket02();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket02.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket02.sale();
           }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket02.sale();
            }
        },"C").start();
    }
}

//lock锁
class Ticket02{
    //属性方法
    private int num =50;
    Lock lock =new ReentrantLock();

    public void sale(){
        //解锁
        lock.lock();
        //业务代码块
        try {
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+num-- +"票"
                        +"剩余了"+num);
            }
        } finally {
            //解锁
            lock.unlock();
        }

    }
}

Lock锁与Synchronized锁的区别

  1. synchronized是一个内置的java关键字,Lock是一个java类
  2. synchronized是无法判断获取锁的状态,Lock是可以判断是否获得锁
  3. synchronized会自动释放锁,lock锁必须要手动释放锁,如果不释放锁就会发生死锁
  4. synchronized线程1(获得锁)和线程2(等待)如果线程1阻塞那么线程2会一直等下去但是lock锁不会一直等待下去。lock.trylock();
  5. synchronized是一个可重入锁、不可以中断的,非公平的:Lock,是一个可重入锁,可以中断的,可以设置公平性。在RenntrantLock(参数“true”或者false)
  6. synchronized适合少量的代码同步问题,lock可以锁大量同步代码

4.生产者和消费者问题

第一种:用synchronized锁来实现生产者和消费者问题:

生产者和消费者都各有一个时:此线程可以正常运行

//通过synchronized来实现生产者和消费者模式
public class Sale01 {

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

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    date.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"P").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    date.sale();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"C01").start();

    }
}
//资源类
class  date{
    //属性
    private  int num=0;


    //方法
    // 生产的方法
    public synchronized void product() throws InterruptedException {
        if(num != 0){
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"==>"+num);
        //通知
        this.notifyAll();
    }


    //方法
    //售卖的方法
    public  synchronized void sale() throws InterruptedException {
            if(num==0){
                //等待
                this.wait();
            }
            num--;

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

            //通知
            this.notifyAll();
    }
}

但是当有4个线程,两个生产者和两个消费者时就会出现问题:出现问题的原因是虚假唤醒

wait()方法:虚假唤醒

java 怎么并发调用别人接口_java_08

解决方案:等待应该一直 存在在while循环中,

将if循环改为while循环即可解决虚假唤醒的问题

//通过synchronized来实现生产者和消费者模式
public class Sale01 {

    public static void main(String[] args) {
        date date = new date();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    date.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"P1").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    date.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"P2").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    date.sale();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"C01").start();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                try {
                    date.sale();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"C02").start();
    }
}
//资源类
//判断等待、业务、通知
class  date{
    //属性
    private  int num=0;
    //方法
    // 生产的方法
    public synchronized void product() throws InterruptedException {
        while(num != 0){
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"==>"+num);
        //通知
        this.notifyAll();
    }


    //方法
    //售卖的方法
    public  synchronized void sale() throws InterruptedException {
            while (num==0){
                //等待
                this.wait();
            }
            num--;
         System.out.println(Thread.currentThread().getName()+"==>"+num);
            //通知
            this.notifyAll();
    }
}
第二种:用Lock锁来实现生产者和消费者问题:

1.传统的锁解决线程通信问题

java 怎么并发调用别人接口_java_09

2.lock锁解决线程通信问题

Condition:监视器

java 怎么并发调用别人接口_i++_10

java 怎么并发调用别人接口_java 怎么并发调用别人接口_11

代码实现:不过此部分的代码实现的功能紧紧跟传统的synchronized锁实现的一样,但是condition并不仅仅代替synchronized锁,任何一个新技术的出现并不仅仅覆盖原来的技术,优势和补充。

import java.nio.file.FileAlreadyExistsException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Sale02 {
    public static void main(String[] args) {
        Date02 date02 = new Date02();

        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                date02.product();
            }
        },"p1").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                date02.product();
            }
        },"p2").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                date02.sale();
            }
        },"c1").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                date02.sale();
            }
        },"c2").start();

    }
}
//资源类
class Date02{
    //属性
    private int num=0;
    //方法创造产品的方法
    Lock lock =new ReentrantLock();
    //condition

    Condition condition = lock.newCondition();
//        condition.await();//等待
//        condition.signalAll();//唤醒

    public void  product(){


        lock.lock();
        try{
            while (num!=0){
                //等待
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //做出产品
            num++;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            //通知
            condition.signalAll();

        }finally {
            lock.unlock();
        }
    }
    public  void sale(){
        lock.lock();
        try {
            while (num==0){
                //等待
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //消费产品
            num--;
            System.out.println(Thread.currentThread().getName()+"==>"+num);
            //通知
        condition.signalAll();

        } finally {
            lock.unlock();
        }
    }

}

以上代码出现的问题:

其线程的执行时随机的,并不是有序的。

p1 —>c1\c2并不是p1–>c1和p2–>c2.

有序执行:精准的通知和唤醒

java 怎么并发调用别人接口_java_12

3.通过condition来做到精准的线程执行

有多个线程A,B,C,通过condition监视器来完成线程的顺序执行A–>B–>C

通过多个监视器去通知

package com.mahui.pc;
/*
* 线程的执行顺序
* A-->B-->C
* */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class A {
    public static void main(String[] args) {
        Date03 date03 = new Date03();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                date03.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                date03.printB();

            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                date03.printC();
            }
        },"C").start();

    }
}

//资源类

class Date03{
        //创建锁
        Lock lock =new ReentrantLock();
        //创建监视器
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        Condition condition3 = lock.newCondition();

        //标志位
        private  int  num =  1;
        //业务的三步:判断-->运行--->通知
        public void printA(){
            //获得锁
            lock.lock();

            try {
                while (num != 1){
                    //等待
                    try {
                        condition1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //改变标志位
                num =2;
                System.out.println(Thread.currentThread().getName()+"===>AAAAAAA");
                //唤醒B线程
                condition2.signal();
            }
            finally {
                lock.unlock();
            }
        }
        public void printB(){
            lock.lock();
            try {
                while(num!=2){
                    //等待
                    try {
                        condition2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //该变标志位
                num=3;
                System.out.println(Thread.currentThread().getName()+"===>"+"BBBBBBBB");

                //唤醒c线程
                condition3.signal();

            }finally {
                lock.unlock();
            }
        }
        public void printC(){
            lock.lock();
            try {
                while (num!=3){
                    //等待
                    try {
                        condition3.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                num=1;
                System.out.println(Thread.currentThread().getName()+"===>"+"CCCCCCC");
                //唤醒线程A
                condition1.signal();
            } finally {
                lock.unlock();
            }
        }
}

运行结果:

java 怎么并发调用别人接口_i++_13

5. 深刻理解锁

Test1

一个对象两个方法加锁

对于以下的代码思考是会进行先打电话还是先发短信呢?

import java.util.concurrent.TimeUnit;

/*
* 对于phone是先打电话还是先发短信
* */
public class Test01 {
    public static void main(String[] args) {
        //synchronized 锁的对象是方法的调用者
        //两个方法用的是用一个锁
        //只有一个对象phone
        Phone phone =new Phone();

        new Thread(()->{
            phone.sendms();

        },"A").start();
		//A执行完之后休眠1秒
        try {
            //import java.util.concurrent.TimeUnit;包下的休息类
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{
    public synchronized void sendms(){
        //休眠四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}

结果是:先发短信然后打电话。

java 怎么并发调用别人接口_java_14

分析原因:

synchronized锁的对象是方法的调用者,在本例方法的调用者是phone,只有一个对象phone,所以两种方法持有一把锁,发短信拿到锁然后释放锁之后打电话才会拿到锁。sleep是不会释放锁的。

如果sleep4秒?

同样的,先是发短信之后是打电话。

Test2

一个对象,一个方法加锁一个不加锁

分析下列代码会先输出什么?

import java.util.concurrent.TimeUnit;

public class Test02 {
    public static void main(String[] args) {
        //synchronized 锁的对象是方法的调用者
       
        Phone02 phone =new Phone02();

        new Thread(()->{
            phone.sendms();

        },"A").start();

        try {
            //import java.util.concurrent.TimeUnit;包下的休息类
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}
class Phone02{
    public synchronized void sendms(){
        //休眠四秒
        
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
    //这里没有锁
    public void hello(){
        System.out.println("hello");
    }
}

结果是:先输出hello后输出发短信

java 怎么并发调用别人接口_java_15

分析原因:在本例中hello方法并没有锁,不受锁的影响,所以它并不需要等待sendms方法执行完之后再去执行,而sendms方法休眠了四秒,所以hello先输出,而发短信后输出。

Test3

两个对象

分析下列代码先输出什么?

import java.util.concurrent.TimeUnit;

public class Test03  {
    public static void main(String[] args) {
        //synchronized 锁的对象是方法的调用者
        //有了两个对象有两把锁
        Phone03 phone1 =new Phone03();
        Phone03 phone2 =new Phone03();
        new Thread(()->{
            phone1.sendms();

        },"A").start();

        try {
            //import java.util.concurrent.TimeUnit;包下的休息类
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone03{
    public synchronized void sendms(){
        //休眠四秒

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}

输出结果是:

java 怎么并发调用别人接口_System_16

分析原因:一定是先输出打电话,后输出发短信,因为这个例子中有两个对象phone1和phone2所以每个对象都有一把锁,在本例中call方法不需要等phone1的锁,它自己有自己的锁,而sendms方法休眠了四秒钟所以,先输出打电话后输出发短信。

Test4

静态方法:一个对象 两个静态方法static判断先输出什么后输出什么?

import java.util.concurrent.TimeUnit;

public class Test04 {
    public static void main(String[] args) {
        
     
        Phone04 phone1 =new Phone04();
        new Thread(()->{
            phone1.sendms();
        },"A").start();
        try {
            //import java.util.concurrent.TimeUnit;包下的休息类
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone1.call();
        },"B").start();
    }
}
class Phone04{
    //synchronized 锁的对象是方法的调用者
    //static 静态方法
    //类一加载就有了!锁的是class 全局唯一所以只有一把锁
    public static synchronized void sendms(){
        //休眠四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}

输出结果:

java 怎么并发调用别人接口_i++_17

分析原因:在该例中,方法使用了static修饰,其是一个静态的方法,类一加载就有的,锁的是class,全局唯一的所以也只有一把锁,所以当sendms方法释放锁之后,call才会获得锁。因此发短信先输出之后是打电话输出。

Test5

问题:两个对象两个静态的方法先输出什么?

import java.util.concurrent.TimeUnit;

public class Test05 {
    public static void main(String[] args) {
        //synchronized 锁的对象是方法的调用者
        //两个方法用的是用一个锁
        //只有一个对象phone
        Phone05 phone1 =new Phone05();
        Phone05 phone2 =new Phone05();
        new Thread(()->{
            phone1.sendms();
        },"A").start();
        try {
            //import java.util.concurrent.TimeUnit;包下的休息类
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone05{
    public static synchronized void sendms(){
        //休眠四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}

输出的结果:

java 怎么并发调用别人接口_java_18

分析原因:加了static之后锁的是class,phone1和phone2的class文件是同一个,所以塔两使用的是同一把锁

所以需要等待sendms方法释放锁之后,call方法才能获得锁

Test6

问题:一个普通的同步方法,一个静态的同步方法,两个对象

package com.mahui.demo03;

import java.util.concurrent.TimeUnit;

public class Test06 {
    public static void main(String[] args) {
        //synchronized 锁的对象是方法的调用者
        //两个方法用的是用一个锁
        //只有一个对象phone
        Phone06 phone1 =new Phone06();
		Phone06 phone2 =new Phone06();
        new Thread(()->{
            phone1.sendms();
        },"A").start();
        try {
            //import java.util.concurrent.TimeUnit;包下的休息类
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone06{
    //静态的同步方法

    public static synchronized void sendms(){
        //休眠四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通的同步方法
    public synchronized void call(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}

结果显示:

java 怎么并发调用别人接口_java 怎么并发调用别人接口_19

分析原因:先发短信后打电话,static 静态方法锁的是class类的锁,在这个例子中有两把锁,一把是class锁,一把是对象的锁,所以他们的锁不一样,谁睡眠的时间长谁就后打印。

总结:锁就两种

new 创建对象出来的一把锁,锁的是调用者this对象

static class唯一的一个,只有一个class文件

6.集合类不安全

并发下集合类不安全

1.list

在多线程下使用Arraylist是不安全的

import java.util.ArrayList;
import java.util.UUID;
public class ListTest {
    public static void main(String[] args) {
        //java.util.ConcurrentModificationException   并发修改异常!
        ArrayList<String> list = new ArrayList<>();

        for (int i =1; i <=10; i++) {
            new Thread(()->{
                list.add(
                        UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

显示输出结果:java.util.ConcurrentModificationException 并发修改异常!

有可能是会报错:

java 怎么并发调用别人接口_java 怎么并发调用别人接口_20

如何解决并发修改异常呢?

1. 使用 List list =new Vector<>();
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

public class ListTest {
    public static void main(String[] args) {
        //java.util.ConcurrentModificationException   并发修改异常!
        //ArrayList<String> list = new ArrayList<>();
        List<String> list =new Vector<>();
        for (int i =1; i <=10; i++) {
            new Thread(()->{
                list.add(
                        UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

为什么Vector可以解决?

Vector add方法使用的是synchronized

java 怎么并发调用别人接口_i++_21

2.使用工具类

List list = Collections.synchronizedList(new ArrayList<>());

package com.mahui.Unsafe;

import java.util.*;

public class ListTest {
    public static void main(String[] args) {
        //java.util.ConcurrentModificationException   并发修改异常!
        //ArrayList<String> list = new ArrayList<>();
       //1. List<String> list =new Vector<>();
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i =1; i <=10; i++) {
            new Thread(()->{
                list.add(
                        UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
3. CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
package com.mahui.Unsafe;

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

public class ListTest {
    public static void main(String[] args) {
        //java.util.ConcurrentModificationException   并发修改异常!
        //ArrayList<String> list = new ArrayList<>();
       //1. List<String> list =new Vector<>();
       //2. List<String> list = Collections.synchronizedList(new ArrayList<>());
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i =1; i <=10; i++) {
            new Thread(()->{
                list.add(
                        UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

为什么CopyOnWriteArrayList可以解决这个问题?

CopyOnWriteArrayList的add 方法使用的是lock锁。这是与Vector方法的本质区别。

add源码:

java 怎么并发调用别人接口_System_22

2.set不安全

java 怎么并发调用别人接口_java 怎么并发调用别人接口_23

在多线程下使用HashSet是不安全的:

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class SetTest {
    public static void main(String[] args) {
        Set<String> set =  new HashSet<>();
        for (int i = 1; i <= 10; i++) {

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

会出现:java.util.ConcurrentModificationException并发修改异常

java 怎么并发调用别人接口_i++_24

如何解决呢?

1.使用工具类将其转换为安全的.
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class SetTest {
    public static void main(String[] args) {
        //  Set<String> set =  new HashSet<>();不安全的

        
        //2.使用工具类安全的

        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        for (int i = 1; i <= 10; i++) {

            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}
2.写入时复制,保证效率
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class SetTest {
    public static void main(String[] args) {
        //  Set<String> set =  new HashSet<>();不安全的
        //2.使用工具类安全的

        //1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 10; i++) {

            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}
hashset的底层是什么?
public HashSet() {
        map = new HashMap<>();
    }

//add  set  本质是map key是无法重复的
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
//SPRESENT是一个不变的值
 private static final Object PRESENT = new Object();
3.Map不安全

java 怎么并发调用别人接口_java_25

package com.mahui.Unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

//java.util.ConcurrentModificationException 并发修改异常
public class MapTest {

    public static void main(String[] args) {
        //map 是这样使用的吗?  Map<String,String> map=new HashMap<>();
        //不是这样使用的,工作中不使用hashmap
        Map<String,String> map=new HashMap<>();
        // 默认等价于?new HashMap<>(16,0.75);

        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
        //加载因子、初始化容量


    }
}

会出现:java.util.ConcurrentModificationException 并发修改异常

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

//java.util.ConcurrentModificationException 并发修改异常
public class MapTest {

    public static void main(String[] args) {
        //map 是这样使用的吗?  Map<String,String> map=new HashMap<>();
        //不是这样使用的,工作中不使用hashmap
        // Map<String,String> map=new HashMap<>();
        // 默认等价于?new HashMap<>(16,0.75);
        Map<String,String> map=new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
        //加载因子、初始化容量
    }
}

2.使用ConcurrentHashMap

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

//java.util.ConcurrentModificationException 并发修改异常
public class MapTest {

    public static void main(String[] args) {
        //map 是这样使用的吗?  Map<String,String> map=new HashMap<>();
        //不是这样使用的,工作中不使用hashmap
        // Map<String,String> map=new HashMap<>();
        // 默认等价于?new HashMap<>(16,0.75);
        Map<String,String> map=new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
        //加载因子、初始化容量
    }
}

7.Callable

java 怎么并发调用别人接口_java_26

通过API文档可以了解到:callable

1.可以有返回值

2.可以抛出异常

3.方法不同,run() call()

在callable中。

java 怎么并发调用别人接口_i++_27

java 怎么并发调用别人接口_i++_28

java 怎么并发调用别人接口_System_29

它的构造方法可以跟runnable和callable挂上关系:

java 怎么并发调用别人接口_i++_30

package com.mahui.Callable;


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

public class CallableTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //实现runnable接口的启动方式        new Thread(new MyThread()).start();
        //callable实现的关系
        //new thread(new Runnable()).start()
        //new thread(new FutureTask<V>()).start()
        //new thread(new FutureTask<V>(callable)).start()
        //callable无法直接调用Runnable 需要一个适配类  该适配类是FutureTask  通过适配类来启动callable
        MyThread myThread = new MyThread();

        FutureTask futureTask =new FutureTask(myThread);//适配类
        new Thread(futureTask).start();

        Integer o = (Integer)futureTask.get();//获取callable返回结果 这个get方法可能会产生阻塞!把他放在最后
        System.out.println(o);
    }
}
class  MyThread implements Callable<Integer>{


    @Override
    public Integer call() throws Exception {
        System.out.println("call方法执行了");
        return 123;
    }
}

运行结果:

java 怎么并发调用别人接口_i++_31

8.常用辅助类

1.CountDownLatch

减法计数器

import java.util.concurrent.CountDownLatch;

//计数器  down减法
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(8);

        for (int i = 1; i <= 8; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"GO  OUT");
                //线程数量-1
                countDownLatch.countDown();
            },String.valueOf(i)).start();

        }
        //等待计数器归0执行完再继续往下执行
        countDownLatch.await();
        System.out.println("close door");

    }
}

原理:

两个方法:

countDownLatch.countDown();数量-1

countDownLatch.await();等待计数器归零,然后再向下执行

每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒

2.CyclicBarrier

加法计数器:

package com.mahui.add;

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

public class CyclicBarrierDome {
    public static void main(String[] args) {
        /*
        * 集齐7颗龙珠
        * */
        //召唤龙珠的线程
        CyclicBarrier cyclicBarrier =new CyclicBarrier(7,()->{
            System.out.println("召唤龙珠成功");
        });

        for (int i = 1; i <= 7; i++) {
            //lambda表达式拿不到i的值 将i设置为fial值即可
            final  int temp =i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");

                try {
                    //等待
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
3.Semaphore

Semaphore:信号量,限流的使用多

例子:6车只有3停车位 如何解决

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

public class SemaphoreDemo {
    public static void main(String[] args) {
        //线程数量,三个停车位
        Semaphore semaphore = new Semaphore(3);
        //6个车
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                //acquire()得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    //停车两秒
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //release()释放
                    semaphore.release();
                }
            }).start();
        }
    }
}

原理:

semaphore.acquire();获取,假设已经满了就等待,等待释放为止

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

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

9.读写锁

java 怎么并发调用别人接口_System_32

读可以被多个线程使用但是写只能是独家的

package com.mahui.rw;



import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * 独占锁(写锁)一次只能一个线程占有
 * 共享锁(读锁)可以多个线程同时占有
 * ReadWriteLock
 * 读-读  可以共存
 * 读-写   不能共存
 * 写-写  不能共存
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();
        //写入
        for (int i = 1; i <=5; i++) {
            final int temp = i;
            new Thread(()->{myCache.put(temp+"", temp+"");},String.valueOf(i)).start();
        }

        //读取
        for (int i = 1; i <= 5; i++) {
            final  int temp =i;
            new Thread(()->{myCache.get(temp+"");},String.valueOf(i)).start();
        }

    }
}
//加锁
class MyCacheLock{
    private  volatile  Map<String,Object> map =new HashMap<>();
    //读写锁,更加细粒度的控制
    private ReadWriteLock readWriteLock =new ReentrantReadWriteLock();
    //存、写 写入的时候同时只有一个线程写入
    public void put(String key,Object value){
        //加一把写锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入ok");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.writeLock().unlock();
        }

    }
    //取、读  所有的线程都可以读
    public void get(String key){
        readWriteLock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取ok");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.readLock().unlock();
        }

    }
}
/**
 * 自定义缓存 没有加锁
 */
class MyCache{
    private  volatile  Map<String,Object> map =new HashMap<>();

    //存、写
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入ok");
    }
    //取、读
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取ok");
    }
}

10.阻塞队列

FIFO:先进先出

不得不阻塞:

写入 :如果队列满了,就必须阻塞等待

取:如果队列是空的必须阻塞等待生产什么情况下我们会使用阻塞队列:多线程并发处理

java 怎么并发调用别人接口_java 怎么并发调用别人接口_33

如何使用队列:添加、移除

方式

抛出异常

不会抛出异常,又返回值

阻塞等待

超时等待

添加

add()

offer()

put()

offer(三个参数)

移除

remove()

poll()

take()

poll(两个参数)

判段队列首

element()

peek()

-

-

1.抛出异常
package com.mahui.bq;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        test.test1();
    }

    /**
     * 抛出异常
     */
    public void test1(){
        //队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));

        //java.lang.IllegalStateException: Queue full 抛出队列已满的异常
        //System.out.println(arrayBlockingQueue.add("d"));
        
          //返回队首元素是谁
        System.out.println(arrayBlockingQueue.element());
        System.out.println("======================================");
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());

        //java.util.NoSuchElementException  抛出异常没有元素的
        // System.out.println(arrayBlockingQueue.remove());

    }
}
2.有返回值
package com.mahui.bq;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        test.test2();
    }

    /**
     * 抛出异常
     */
    public void test1(){
        //队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));

        //java.lang.IllegalStateException: Queue full 抛出队列已满的异常
        //System.out.println(arrayBlockingQueue.add("d"));
            //返回队首元素是谁
        System.out.println(arrayBlockingQueue.element()); System.out.println("======================================");
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());

        //java.util.NoSuchElementException  抛出异常没有元素的
        // System.out.println(arrayBlockingQueue.remove());

    }
     /**
     * 又返回值不抛出异常
     */

    public void  test2(){
        ArrayBlockingQueue arrayBlockingQueue =new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));
        //队列满了之后输出false 不跑出异常
        System.out.println(arrayBlockingQueue.offer("d"));

        System.out.println(arrayBlockingQueue.poll());
          //检测队首元素
        System.out.println(arrayBlockingQueue.peek());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());

        //返回null  不抛出异常
        System.out.println(arrayBlockingQueue.poll());

    }
}
3.阻塞等待
public void test3() throws InterruptedException {
    //队列大小
    ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
    //一直阻塞  没有返回值
    arrayBlockingQueue.put("a");
    arrayBlockingQueue.put("b");
    arrayBlockingQueue.put("c");
    //队列没有位置了,一直阻塞程序不执行
    //arrayBlockingQueue.put("d");

    System.out.println(arrayBlockingQueue.take());
    System.out.println(arrayBlockingQueue.take());
    System.out.println(arrayBlockingQueue.take());
    //没有元素会一直等待
    //System.out.println(arrayBlockingQueue.take());


}
4.超时等待
/**
 * 等待,阻塞(等待超时,超过时间就不等了)
 */
public void test4() throws InterruptedException {
    ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

    arrayBlockingQueue.offer("a");
    arrayBlockingQueue.offer("b");
    arrayBlockingQueue.offer("c");
    //等待等待两秒如果还是没有位置就会退出
    arrayBlockingQueue.offer("d",2,TimeUnit.SECONDS);
    System.out.println("=============================================");
    System.out.println(arrayBlockingQueue.poll());
    System.out.println(arrayBlockingQueue.poll());
    System.out.println(arrayBlockingQueue.poll());

    //取不出来 等待两秒结束
    System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
}

SynchronousQueue 同步队列

没有容量的,进去一个元素,必须等待取出来之后,才能往里面放一个元素!

对应的方法 put() take()

package com.mahui.bq;

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

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        //同步队列
        SynchronousQueue<String> blockingQueue = new SynchronousQueue<>();
        //
        new Thread(()->{

                try {
                    System.out.println(Thread.currentThread().getName()+"put 1");
                    blockingQueue.put("1");
                    System.out.println(Thread.currentThread().getName()+"put 2");
                    blockingQueue.put("2");
                    System.out.println(Thread.currentThread().getName()+"put 3");
                    blockingQueue.put("3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

        },"T1").start();
        //
        new Thread(()->{

                try {
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+blockingQueue.take());
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+blockingQueue.take());
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+blockingQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

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

结果显示:

java 怎么并发调用别人接口_System_34

一个线程写入一个元素,必须等待下一个线程把它取出来,否则不能在put进去

11.线程池

池化技术

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

线程池、连接池、内存池、对象池…

池化技术:事先准备好的一些资源,需要用时来拿,用完之后还回来。

线程池的优点:

1.降低资源的消耗

2.提高响应速度

3.方便管理。

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

1.线程池三大方法:

创建线程的三大方法:

  1. 单个线程
  2. 固定数目的线程池
  3. 可伸缩的线程池。
package com.mahui.pool;

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

public class Demo01 {
    public static void main(String[] args) {
        //1.单个线程
        //ExecutorService threadpool = Executors.newSingleThreadExecutor();
        //2.创建一个固定的线程池大小
         // ExecutorService threadpool =  Executors.newFixedThreadPool(5);
        //3.可伸缩的,遇强则强,遇弱则弱
         ExecutorService threadpool =Executors.newCachedThreadPool();
        try{
            //线程池用完,程序结束,关闭线程池
            for (int i = 1; i <= 10; i++) {
                //使用线程池后,使用线程池创建线程
                threadpool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭线程池
            threadpool.shutdown();
        }
    }
}
2.7大参数:

创建单一线程的源码:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                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,//21亿
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

通过分析:开启线程调用的是ThreadPoolExecutor

ThreadPoolExecutor的源码:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

//进入this方法
  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;
    }

java 怎么并发调用别人接口_java_35

3.手动创建线程池ThreadPoolExecutor
public class Demo02 {
    public static void main(String[] args) {
        //自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.MILLISECONDS,
                //阻塞队列
                new LinkedBlockingDeque<>(3),
                //默认的
                Executors.defaultThreadFactory(),
                //默认的拒绝策略,如果对队列满了,还有需要进来的,就不处理个抛出异常
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
4.四种拒绝策略

java 怎么并发调用别人接口_System_36

1.AbortPolicy()默认的拒绝策略
package com.mahui.pool;
import java.util.concurrent.*;
public class Demo02 {
    public static void main(String[] args) {
        //自定义线程池
        ExecutorService threadPool= new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.MILLISECONDS,
                //阻塞队列
                new LinkedBlockingDeque<>(3),
                //默认的
                Executors.defaultThreadFactory(),
                //默认的拒绝策略,如果对队列满了,还有需要进来的,就不处理这个 ,抛出异常
                new ThreadPoolExecutor.AbortPolicy()
        );
        try {
            //最大量maximumPoolSize+队列容量 超出最大容量会抛出异常RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName()+"ok");
            });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}
2.CallerRunsPolicy()哪里来的回哪去,不会抛出异常
package com.mahui.pool;

import java.util.concurrent.*;

public class Demo03 {
    public static void main(String[] args) {
        //自定义线程池
        ExecutorService threadPool= new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.MILLISECONDS,
                //阻塞队列
                new LinkedBlockingDeque<>(3),
                //默认的
                Executors.defaultThreadFactory(),
                //哪来的就回哪里去main线程执行
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        try {
            //最大量maximumPoolSize+队列容量 超出最大容量会抛出异常RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

java 怎么并发调用别人接口_System_37

3.DiscardPolicy() 队列满了它不会抛出异常,丢掉任务
package com.mahui.pool;

import java.util.concurrent.*;

public class Demo04 {
    public static void main(String[] args) {
        //自定义线程池
        ExecutorService threadPool= new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.MILLISECONDS,
                //阻塞队列
                new LinkedBlockingDeque<>(3),
                //默认的
                Executors.defaultThreadFactory(),
                //队列满了它不会抛出异常,丢掉任务。
                new ThreadPoolExecutor.DiscardPolicy()
        );
        try {
            //最大量maximumPoolSize+队列容量 超出最大容量会抛出异常RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

运行结果:只运行了八个,有一个不运行,不抛出

java 怎么并发调用别人接口_System_38

4.DiscardOldestPolicy() 队列满了,会尝试跟第一个竞争,看第一个是否完成,如果竞争到就会运行,没竞争到就丢掉,也不会抛出异常
package com.mahui.pool;

import java.util.concurrent.*;

public class Demo05 {
    public static void main(String[] args) {
        //自定义线程池
        ExecutorService threadPool= new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.MILLISECONDS,
                //阻塞队列
                new LinkedBlockingDeque<>(3),
                //默认的
                Executors.defaultThreadFactory(),
                //队列满了,会尝试跟第一个竞争,看第一个是否完成,如果竞争到就会运行,没竞争到就丢掉,也不会抛出异常

                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
        try {
            //最大量maximumPoolSize+队列容量 超出最大容量会抛出异常RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}
最大线程到底应如何定义?(调优的)

1.CPU密集型,最大线程数几核就定义为几,保证效率最高

java 怎么并发调用别人接口_System_39

  • 通过代码获取CPU的核数
Runtime.getRuntime().availableProcessors()

2.IO密集型:判断你程序中十分耗IO的线程

一个程序有15个大型人物,io十分占用资源,

12.四大函数式接口

jdk8的新特性:lambda表达式、链式编程、函数式接口、Stream流式计算

函数式接口:只有一个方法的接口,简化编程模型

例子:runnable接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

//FunctionalInterface函数式接口
//简化编程模型
1.Function函数式接口

java 怎么并发调用别人接口_System_40

代码实现:

package com.mahui.Function;

import java.util.function.Function;

public class Demo01 {
    public static void main(String[] args) {
        //Function  代码实现类 匿名内部类
      /*  Function function =new Function<String,String>() {
            @Override
            public String apply(String string) {
                return string;
            }
        };*/
      //lambda表达式来实现
        Function<String,String> function =(str)->{
          return str;
        };
        System.out.println(function.apply("abcd"));
    }
}
2.predicate断定型接口

java 怎么并发调用别人接口_java_41

import java.util.function.Predicate;
/*
* 断定型接口:有一个输入参数,返回值只能是boolean值
* */
public class Demo02 {
    public static void main(String[] args) {
        //判断字符串是否为空
       /*Predicate<String> predicate= new Predicate<String>() {
           @Override
           public boolean test(String string) {

               return string.isEmpty();
           }
       };*/
       //lambda表达式
        Predicate<String> predicate =(str)->{return str.isEmpty();};
        System.out.println(predicate.test(""));
    }
}
3.Consumer消费型接口

java 怎么并发调用别人接口_java 怎么并发调用别人接口_42

package com.mahui.Function;

import java.util.function.Consumer;
/*
* Consumer 消费型接口:只有输入,没有返回值
* */
public class Demo03 {
    public static void main(String[] args) {
        //消费型接口
       /* Consumer<String> consumer =new Consumer<String> () {
            @Override
            public void accept(String string) {
                System.out.println(string);
            }
        };*/
        //lambda表达式
        Consumer<String> consumer=(string)->{
            System.out.println(string);
        };
        consumer.accept("abcd");
    }
}
4.Supplier 供给型接口

java 怎么并发调用别人接口_java_43

package com.mahui.Function;

import java.util.function.Supplier;
/*
* 供给型接口:没有参数,只有返回值
* */

public class Demo04 {
    public static void main(String[] args) {
       /* Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get(  ) {
                return "abcd";
            }
        };*/
        //lambda表达式
        Supplier<String> supplier =()->{return  "abcd";};
        System.out.println(supplier.get());

    }
}

13.Stream流式计算

java 怎么并发调用别人接口_java 怎么并发调用别人接口_44

1.什么是Stream流式计算

集合跟MySql的本质是用来存储数据的,而stream是用来计算的。

User类

package com.mahui.stream;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor//无参构造
@AllArgsConstructor//有参构造
public class User {
    private int id;
    private String name;
    private int age;
}

测试类:

通过流来计算:

package com.mahui.stream;

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

/**
 * 题目要求:
 * 现在有六个用户!筛选
 * 1.id必须是偶数
 * 2.年龄必须大于23岁
 * 3.用户名转换为大写字母
 * 4.用户名字母倒着排序
 * 5.只输出一个用户
 */

public class Test {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 22);
        User u3 = new User(3, "c", 23);
        User u4 = new User(4, "d", 24);
        User u5 = new User(5, "e", 25);
        User u6 = new User(6, "f", 26);
        //用集合将其存储起来
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5, u6);
        //将集合转换为流进行计算
        //链式编程
        list.stream()
                //过滤出id为偶数的用户
                .filter((u)->{return  u.getId()%2==0;})
                //过滤出年龄大于23的用户
                .filter((u)->{return u.getAge()>23;})
                //用户名字转换为大写字母
                .map((u)->{return  u.getName().toUpperCase();})
                //用户名字反着排序
                .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
                //只返回一个用户
                .limit(1)
                //遍历用户
        .forEach(System.out::println);
    }
}

14.ForKJoin(分支合并)

1.什么事ForKJoin

Forkjoin在jdk1.7出现的,并行执行任务!提高效率,大数据量!

将一个任务拆分为多个小任务。每个小任务都有一个结果,将他们的结果合并起来为最终的结果。

2.forkjoin的特点

这里维护的是双端队列从两头都可以执行的队列

工作窃取:一个线程执行完之后,其他线程还没有执行完,这个线程会将其他线程没有执行完的地方拿来执行。

3.如何使用forkjoin

java 怎么并发调用别人接口_java_45

forkjoin类

import java.util.concurrent.RecursiveTask;

/***
 *求和计算
 * 如何使用forkjoin
 * 1.forkjoinpool
 * 2.需要继承RecursiveTask
 * 3.原理就是递归思想
 */
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 task1 = new ForkjoinDemo(start, middle);
            //拆分任务,把任务压入线程队列
            task1.fork();
            ForkjoinDemo task2 = new ForkjoinDemo(middle + 1, end);
            //拆分任务
            task2.fork();
            //返回task1的值和task2的值的和
            return  task1.join()+task2.join();
        }
    }
}

测试类,通过三种方法来计算

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

/**
 * 三种方法计算和:
 * 1.使用传统的计算        9703
 * 2.使用forkjoin计算     8039
 * 创建forkjoinpool
 * 通过forkjoinpool来执行
 * 3.使用stream流计算     310
 */

public class Test1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1();
        //test2();
        test3();
    }
    public  static void  test1(){
        long start = System.currentTimeMillis();
        long sum =0L;
        for (Long i = 1L; i <=10_0000_0000; i++) {
            sum+=i;
        }

       long end = System.currentTimeMillis();


        System.out.println("运行的结果是:"+sum+"耗费的时间是"+(end-start));
    }
    public  static void  test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkjoinDemo(0L,10_0000_0000L);
        //执行任务,没有结果
       // forkJoinPool.execute(task);
        //Long sum = task.get();
        //提交任务,是有结果的
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("运行的结果是:"+sum+"耗费的时间是"+(end-start));
    }
    public  static void  test3(){
        long start = System.currentTimeMillis();
        //stream 并行流
        //LongStream.range()是开区间()

        //  LongStream.rangeClosed() 是闭区间(]

        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("运行的结果是:"+sum+"耗费的时间是"+(end-start));
    }
}

15.异步回调

异步回调,java中通过future来实现

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
 * 异步调用  ajax
 * 异步执行
 * 成功回调
 * 失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //发起一个请求
        //没有返回值的runAsync异步回调
      /*  CompletableFuture<Void> future =CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"runAsync=>void");
        });
        System.out.println("1111");
        future.get();//因为有延迟,获取阻塞执行结果,如果没有延迟就会直接拿到结果*/
      //有返回值的 supplyAsync   异步回调
        //ajax  成功和失败的回调
        //失败了ajax返回的是错误信息
        CompletableFuture<Integer> future  =CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"suppilyAsync=>Integer");
            //故意制造错误  分母不能为0
            int i =10/0;
           //成功了有成功的返回值1024
            return  1024;
        });
        System.out.println(future.whenComplete((t, u) -> {
            //t为正常执行的返回结果
            System.out.println("t=>" + t);
            //u为失败执行后的错误信息u=>java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
            System.out.println("u=>" + u);
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            //有错误了可以返回出错误的结果233
            return 233;
        }).get());
    }
}

16.JMM

1.什么事JMM

JVM:java虚拟机

JMM:Java的内存模型,不存在的东西,是一个概念、约定。

2.关于JMM的一些同步的约定

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

线程A想拿到主存中的变量X,先从主存中复制一份放入线程A的工作内存中,待线程A操作完成后将改变后的变量X应立即刷入主存中。

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

3.加锁和解锁是同一把锁

对原子的八种操作:

java 怎么并发调用别人接口_java 怎么并发调用别人接口_46

八种操作的解释:

java 怎么并发调用别人接口_i++_47

示例:

package com.mahui.jmm;

import java.util.concurrent.TimeUnit;

public class JmmDemo {
    private  static  int num =0;
    public static void main(String[] args) {//主线程也是一条线程

        //线程1
        new Thread(()->{
            //num=0时让线程一直运行
            while (num==0){

            }
        }).start();//开启一个线程
        //休眠两秒使线程1拿到num=0
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num=1;
        System.out.println(num);
    }
}

运行结果:

java 怎么并发调用别人接口_i++_48

分析出现线程运行不停止的原因:

java 怎么并发调用别人接口_java_49

存在问题:程序并不知道主存的值已经被修改过了,需要解决这个问题就需要volatile

17.Volatile

1.什么是Volatile

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

2.volatile的三大特性
  • 保证可见性
package com.mahui.jmm;

import java.util.concurrent.TimeUnit;

public class JmmDemo {
    //不加volatile程序就会死循环
    //加了volatile程序会在输出1之后立即停止
    //所以加了volatile保证了可见性,线程1可以知道num的值发生了变化
    private  volatile  static  int num =0;
    public static void main(String[] args) {//主线程也是一条线程

        //线程1  对主内存的变化是不知道的
        new Thread(()->{
            //num=0时让线程一直运行
            while (num==0){

            }
        }).start();//开启一个线程
        //休眠两秒使线程1拿到num=0
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num=1;
        System.out.println(num);
    }
}
  • 不保证原子性

原子性:不可分割

线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败

案例:结果不为20000 不正确

public class volatileDemo {
    private  static  int num = 0;
    public   static void  add(){
        num++;
    }
    public static void main(String[] args) {
        //理论上num=20000
        //实际上num并不是20000小于20000
        //
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                        add();
                }
            }).start();

        }
        //判断线程是否执行完没
        //大于两个线程main  跟  gc 线程
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

add()增加synchronized 结果为20000正确

保证原子性

public class volatileDemo {
    private  static  int num = 0;
    public synchronized   static void  add(){
        num++;
    }
    public static void main(String[] args) {
        //理论上num=20000
        //实际上num并不是20000小于20000
        //
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                        add();
                }
            }).start();

        }
        //判断线程是否执行完没
        //大于两个线程main  跟  gc 线程
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

给变量用volatile修饰 结果不正确 不为20000

不保证原子性

package com.mahui.jmm;

public class volatileDemo {
    private  volatile  static  int num = 0;
    public    static void  add(){
        num++;
    }
    public static void main(String[] args) {
        //理论上num=20000
        //实际上num并不是20000小于20000
        //
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                        add();
                }
            }).start();

        }
        //判断线程是否执行完没
        //大于两个线程main  跟  gc 线程
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

java 怎么并发调用别人接口_java 怎么并发调用别人接口_50

如果不加lock跟synchronized锁,如何保证原子性?

使用原子类来保证原子性:

java 怎么并发调用别人接口_System_51

package com.mahui.jmm;

import java.util.concurrent.atomic.AtomicInteger;

public class volatileDemo {
    //使用原子类
    private  volatile  static AtomicInteger num = new AtomicInteger();
    public   static void  add(){
        num.getAndIncrement();//AtomicInteger +1
    }
    public static void main(String[] args) {
        //理论上num=20000
        //实际上num并不是20000小于20000
        //
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                        add();
                }
            }).start();

        }
        //判断线程是否执行完没
        //大于两个线程main  跟  gc 线程
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

运行结果:正确

java 怎么并发调用别人接口_java_52

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

Unsafe类是一个很特殊的存在

  • 禁止指令重排

1.什么是指令重排?

你写的程序,计算机并不是按照你写的那样去执行的。

程序运行的过程:

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

**处理器在进行指令重排的时候要考虑数据之间的依赖性!**并不会进行乱排序

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

我们所期望的执行顺序:1234  
但是可能执行的时候是是:2134   1324
但是绝不可能是:4123!
原因是:处理器在进行指令重排的时候要考虑数据之间的依赖性!

案例:

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

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

volatile可以避免指令重排:

1.保证特定的操作的执行顺序!

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

java 怎么并发调用别人接口_i++_53

18.深入理解CAS

1.什么是CAS

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

缺点:

  1. 由于底层是自旋锁,循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. 会存在ABA问题

java 怎么并发调用别人接口_System_54

Unsafe类

java 怎么并发调用别人接口_i++_55

执行+1的操作:

java 怎么并发调用别人接口_java 怎么并发调用别人接口_56

java 怎么并发调用别人接口_java 怎么并发调用别人接口_57

package com.mahui.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CasDemo {

    //CAS  compareAndSet 缩写  比较并交换
    public static void main(String[] args) {
        //原子类
        AtomicInteger  atomicInteger=  new AtomicInteger(2020);
        //两个参数第一个期望,第二个更新的值
       // public final boolean compareAndSet(int expectedValue, int newValue)
        //如果我期望的值达到了 那么就更新,否则就不更新 CAS是CPU的并发原语!cpu的指令
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        //
        // atomicInteger.getAndIncrement();//++  执行+1操作
        //false 期望值已经变为2021 并不是2020
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

19.原子引用

解决ABA问题,就使用原子引用,思想是乐观锁。

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

java 怎么并发调用别人接口_System_58

代码演示:

package com.mahui.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class AbaDemo {

    //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());
        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());
        //false 期望值已经变为2021 并不是2020
        //====自己期望的线程
        System.out.println(atomicInteger.compareAndSet(2020, 99999));
        System.out.println(atomicInteger.get());
    }
}

如何解决这个问题:需要原子引用!带版本号的原子操作!

java 怎么并发调用别人接口_i++_59

使用Atomicrerfence类来解决!

java 怎么并发调用别人接口_i++_60

java 怎么并发调用别人接口_java 怎么并发调用别人接口_61

Integer包装类的坑:

java 怎么并发调用别人接口_System_62

使用版本号的原子引用代码演示:

package com.mahui.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

public class AbaDemo02 {
    public static void main(String[] args){
        //泛型是Integer 是在-128 至 127 范围内的赋值 所以其期望值应该在-127—128之间  包装类要注意对象的引用问题
        //一般泛型的引用都是一个对象
        
        AtomicStampedReference<Integer> atomicInteger= new AtomicStampedReference<>(11,1);

//跟乐观锁一样的
        new Thread(()->{
            //获得版本号
            int stamp = atomicInteger.getStamp();
            System.out.println("A1==>"+stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //cas  操作
            System.out.println(atomicInteger.compareAndSet(11, 2,
                    atomicInteger.getStamp(), atomicInteger.getStamp() + 1));

            //输出版本号
            System.out.println("A2==>"+atomicInteger.getStamp());

            System.out.println(atomicInteger.compareAndSet(2, 11,
                    atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            //输出版本号
            System.out.println("A3==>"+atomicInteger.getStamp());


        },"A").start();
        new Thread(()->{
            //获得版本号
            int stamp = atomicInteger.getStamp();
            System.out.println("B1==>"+stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(11, 6,
                    atomicInteger.getStamp(), atomicInteger.getStamp() + 1));

            //输出版本号
            System.out.println("B2==>"+atomicInteger.getStamp());
        },"B").start();

    }
}

运行结果:

java 怎么并发调用别人接口_System_63

20.锁

1.公平锁,非公平锁

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

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

2.乐观锁 悲观锁
3.可重入锁

所有的锁都是可重入锁(递归锁)

4.自旋锁
5.死锁

死锁测试:如何测试死锁?

排查问题:

1.日志

2.堆栈排查