Juc 并发编程学习笔记。

什么是JUC
juc 就是java.util,concurrent 工具包的简称,这是一个处理线程的工具包。
什么是线程和进程
线程:线程作为资源调度的基本单位,是程序的执行单位,执行路径(单线程:一条执行路径,多线程:多条执行路径)是程序使用Cpu 的最基本的单位。
进程:进程就是程序的一次执行,进程是一个程序及其数据在处理机上顺序执行时所发生的活动。它是系统进行资源丰沛和调度的单位。
在这里插入图片描述
在电脑的cpu 使用情况看,一个进程可能会有多个线程。至少也是一个!。

Java 真的可以开启线程吗?

不可以,看源码。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
java中被native 所修饰的,都是本地的方法,是底层C++所写的。我们没有办法通过Java 获取到。

并行和并发
并行:同一时间段,多个线程操作同一个资源。

并发:单位时间内,多个人一起。
线程的几种状态
在这里插入图片描述
是个枚举类

    public enum State {
       
         //创建状态
        NEW,

   
         //运行时状态
        RUNNABLE,

     
         //锁状态
        BLOCKED,
//等待状态
    
        WAITING,

   //超时等待
        TIMED_WAITING,

       //终止状态
        TERMINATED;
    }

sleep 和wait 的区别?
1,来自不同的类
wait ⇒ Object
sleep=> Thread
2,关于锁的释放
wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会睡觉!
3,使用的范围时不同的
wait :必须在同步代码块中
sleep 可以在任何地方睡。
传统的synchronized 锁
案例demo 我们来模拟一下卖票的案例,在我们不加锁的情况下会出现冲突

package com.jj.demo;

/**
 * @author fjj
 * @date 2021/3/18 18:53
 */
public class Demo {
    public static void main(String[] args) {
    //开启多线程卖票
        tickets tickets = new tickets();
        //使用lambda 表达式
        new Thread(()->{
            for (int i = 1; i < 50; i++) {
              tickets.save();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 1; i < 50; i++) {
                tickets.save();
            }
        },"B").start();

    }

}
class tickets{
        //定义我们一共有多少张票
        private int number =40;
    //卖票的方法
    public void  save(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"还有"+number +"张票");
        }

    }
        }

结果
在这里插入图片描述
错的一塌糊涂。
我们可以加一个关键字synchronized
在这里插入图片描述
结果
在这里插入图片描述
不会出现错的情况,这是加最简单的一种锁的方式。
但是我们比没有用到我们的JUC 所以我们需要使用Juc 有关的。
LOCK 锁的使用

JDK 1.8 的api 文档
在这里插入图片描述
Lock 的三个实现类
在这里插入图片描述
在这里插入图片描述
公平锁: 十分公平;可以先来后到
非公平锁:十分不公平:可以插队(默认)
还是原来的代码 只是用了lock 锁

package com.jj.demo;

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

/**
 * @author fjj
 * @date 2021/3/19 18:52
 */
public class Demo1 {
    public static void main(String[] args) {
        //开启多线程卖票
        tickets1 tickets = new tickets1();
        //使用lambda 表达式
        new Thread(()->{ for (int i = 1; i < 50; i++) tickets.save(); },"A").start();
        new Thread(()->{ for (int i = 1; i < 50; i++) tickets.save(); },"B").start();

    }

}
class tickets1{
    //定义我们一共有多少张票
    private int number =40;
    //实现LOCK
   Lock lock= new ReentrantLock();
    //卖票的方法
    public void  save(){
        //加锁
        lock.lock();

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

    }
}

lock 锁使用的三个步骤

1,使用实现类//
2,lock .lock() //加锁
3,在finally  块里解锁 //

两个都可以实现,但是 lock 和synchronized 还是有区别的?

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

synchronized 版的生产者和消费者测试

package com.jj.demo.Producer;

/**
 * @author fjj
 * @date 2021/3/19 20:12
 */
public class Demo1 {
    public static void main(String[] args) {
        A a = new A();
        //开启线程
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class A{
    //定义一个数字
    private int number =0;
    //+1 的操作
    public synchronized void incr() throws Exception {
        if (number!=0){
            //如果不等于0 就让线程等待
            this.wait();
        }
        number++;
        System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
        //通知其他的线程 我加1 完毕
        this.notifyAll();
    }
    //同理 -1 的操作
    public synchronized void decr() throws Exception {
        if (number==0){
            //如果不等于0 就让线程等待
            this.wait();
        }
        number--;
        System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
        //通知其他的线程 我加1 完毕
        this.notifyAll();
    }
}

结果
在这里插入图片描述
这样看没有错误,但是 如果是在来几个线程的话,就会出现问题

package com.jj.demo.Producer;

/**
 * @author fjj
 * @date 2021/3/19 20:12
 */
public class Demo1 {
    public static void main(String[] args) {
        A a = new A();
        //开启线程
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class A{
    //定义一个数字
    private int number =0;
    //+1 的操作
    public synchronized void incr() throws Exception {
        if (number!=0){
            //如果不等于0 就让线程等待
            this.wait();
        }
        number++;
        System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
        //通知其他的线程 我加1 完毕
        this.notifyAll();
    }
    //同理 -1 的操作
    public synchronized void decr() throws Exception {
        if (number==0){
            //如果不等于0 就让线程等待
            this.wait();
        }
        number--;
        System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
        //通知其他的线程 我加1 完毕
        this.notifyAll();
    }
}

我又加了2条线程就出现了错误
在这里插入图片描述
这是因为我们在用if 判断的时候出现了虚假判断,按照官方文档我们需要更改一下判断的条件,不可以用if 判断,我们应该用的是while

package com.jj.demo.Producer;

/**
 * @author fjj
 * @date 2021/3/19 20:12
 */
public class Demo1 {
    public static void main(String[] args) {
        A a = new A();
        //开启线程
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    a.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class A{
    //定义一个数字
    private int number =0;
    //+1 的操作
    public synchronized void incr() throws Exception {
        while (number!=0){
            //如果不等于0 就让线程等待
            this.wait();
        }
        number++;
        System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
        //通知其他的线程 我加1 完毕
        this.notifyAll();
    }
    //同理 -1 的操作
    public synchronized void decr() throws Exception {
        while (number==0){
            //如果不等于0 就让线程等待
            this.wait();
        }
        number--;
        System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
        //通知其他的线程 我加1 完毕
        this.notifyAll();
    }
}

改为while 判断的时候就好了
在这里插入图片描述
Lock 锁的生产者消费者的案例

package com.jj.demo.Producer;

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

/**
 * @author fjj
 * @date 2021/3/19 20:53
 */
public class Demo2 {
    public static void main(String[] args) {
        B b = new B();
        //开启线程
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    b.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    b.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    b.incr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    b.decr();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class B{
    //定义一个数字
    private int number =0;
    //获取Lock
    Lock lock =new ReentrantLock();
    Condition condition = lock.newCondition();
    //+1 的操作
    public  void incr() throws Exception {
        lock.lock();
        try {
            while (number!=0){
                //如果不等于0 就让线程等待
                condition.await();
            }
            number++;
            System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
            //通知其他的线程 我加1 完毕
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //同理 -1 的操作
    public  void decr() throws Exception {
        lock.lock();
        try {
            while (number==0){
                //如果不等于0 就让线程等待
                condition.await();
            }
            number--;
            System.out.println("是"+Thread.currentThread().getName()+"在加数据"+number);
            //通知其他的线程 我加1 完毕
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

实现的类不同还有方法也不同了,不过结果
在这里插入图片描述
不是有序排列的。
设置指定唤醒的锁

package com.jj.demo.Producer;

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

/**
 * @author fjj
 * @date 2021/3/20 14:26
 */
public class Demo3 {
    public static void main(String[] args) {
        //获取对象
        C c = new C();
        //创建线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
               c.printA();
            }
        },"A").start();
        //创建线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                c.printB();
            }
        },"B").start();
    }
}
//需求 当A=1 时线程A执行,否侧是B
class C{
    //定义一个数字用来判断
    private int number =1;
    private Lock lock=new ReentrantLock();
    Condition condition = lock.newCondition();
    Condition condition1 = lock.newCondition();
    public void printA(){
       //开启锁
        lock.lock();
        try {
            //业务代码
            while (number!=1){
                //等待
                condition.await();
            }

            System.out.println(Thread.currentThread().getName()+"A");
            //唤醒指定的锁
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }


    }
    public void printB(){
        //开启锁
        lock.lock();
        try {
            //业务代码
            while (number<1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"B");
            //唤醒指定的锁
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }


    }

}

锁的8个问题

这是一个正常得加锁得案例

package com.jj.demo;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) {
        // 获取对象得实例
        phone phone = new phone();
        //创建线程A,B
        new Thread(()->{
            phone.send();
        },"A").start();
        new Thread(()->{
            phone.call();
        },"B").start();
    }


}
//电话类
class phone{
    //发短信得方法
    public synchronized  void send(){
        System.out.println("我是发短信得功能");
    }
//打电话得功能
    public synchronized  void call(){
        System.out.println("我是打电话功能");
    }
}

结果
在这里插入图片描述

问题一。现在有一个电话类,两个方法。一个发短信,一个打电话方法。假设现在让 线程A睡眠 1 s 是先执行发短信还是先执行 打电话方法?

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws Exception {
        // 获取对象得实例
        phone phone = new phone();
        //创建线程A,B
        new Thread(()->{ phone.send(); },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{ phone.call(); },"B").start();
    }


}
//电话类
class phone{
    //发短信得方法
    public synchronized  void send(){
        System.out.println("我是发短信得功能");
    }
//打电话得功能
    public synchronized  void call(){
        System.out.println("我是打电话功能");
    }
}

结果
在这里插入图片描述
问题二,让在方法里让发短信得方法睡觉 4S 结果会是什么?

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws InterruptedException {
        // 获取对象得实例
        phone phone = new phone();
        //创建线程A,B
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{ phone.call(); },"B").start();
    }


}
//电话类
class phone{
    //发短信得方法
    public synchronized  void send() throws Exception {
        System.out.println("我是发短信得功能");
        //线程睡眠
        TimeUnit.SECONDS.sleep(4);
    }
//打电话得功能
    public synchronized  void call(){
        System.out.println("我是打电话功能");
    }
}

结果
在这里插入图片描述
synchronized 锁得是调用得同一个调用得调用者。原因是因为线程A先拿到了锁。就算睡眠也是抱着锁睡觉得。因为是它先拿到得锁
问题三,当我们在phone 里加了一个没有加锁得方法。此时先执行得就不是发短信 ps(估计我的cpu 太快了,我的居然还是发短信。。。)

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws InterruptedException {
        // 获取对象得实例
        phone phone = new phone();
        //创建线程A,B
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);

        new  Thread(()->{phone.hello();},"B").start();

    }


}
//电话类
class phone{
    //发短信得方法
    public synchronized  void send() throws Exception {
        System.out.println("我是发短信得功能");
        //线程睡眠
        TimeUnit.SECONDS.sleep(4);
    }

    //你好得方法
    public void hello(){
        System.out.println("我是没有加锁的你好方法");
    }
}

问题四,现在有两个对象,一个对象调用 send 方法,一个调用打电话,同理 还是休息4秒

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws InterruptedException {
        // 获取对象得实例
        phone phone = new phone();
        phone phone1 = new phone();
        //创建线程A,B
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);

        new  Thread(()->{phone1.call();},"B").start();

    }


}
//电话类
class phone{
    //发短信得方法
    public synchronized  void send() throws Exception {
        System.out.println("我是发短信得功能");
        //线程睡眠
        TimeUnit.SECONDS.sleep(100);
    }

    //打电话
    public synchronized void call(){
        System.out.println("打电话");
    }
}

我的结果居然还是发短信,我哭了。道理应该是不同得对象,发短信线程还在睡觉应该是打电话先走。但是我的电脑就不让打电话得方法拿到锁。哭了。。。。。。。
问题五,六 。当两个方法都被static 给修饰后,相当于锁得是类了。所以无论与对象调用已经无关了,锁得是phone 类了。

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws InterruptedException {
        // 获取对象得实例
        phone phone = new phone();
        phone phone1 = new phone();
        //创建线程A,B
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);

        new  Thread(()->{phone1.call();},"B").start();

    }


}
//电话类
class phone{
    //发短信得方法
    public static synchronized  void send() throws Exception {
        System.out.println("我是发短信得功能");
        //线程睡眠
        TimeUnit.SECONDS.sleep(4);
    }

    //打电话
    public static synchronized void call(){
        System.out.println("打电话");
    }
}

问题七,当一个方法是static 一个方法没有被static 所修饰得时候。这个时候就是打电话得方法先出来, 因为不是同一把锁了

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws InterruptedException {
        // 获取对象得实例
        phone phone = new phone();

        //创建线程A,B
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);

        new  Thread(()->{phone.call();},"B").start();

    }


}
//电话类
class phone{
    //发短信得方法
    public static synchronized  void send() throws Exception {
        System.out.println("我是发短信得功能");
        //线程睡眠
        TimeUnit.SECONDS.sleep(4);
    }

    //打电话
    public  synchronized void call(){
        System.out.println("打电话");
    }
}

问题八,当我们有两个对象得时候,还是一个是static 修饰,一个是 普通得锁

package com.jj.demo;

import java.util.concurrent.TimeUnit;

/**
 * @author fjj
 * @date 2021/3/20 14:48
 */
public class Lock8 {
    public static void main(String[] args) throws InterruptedException {
        // 获取对象得实例
        phone phone = new phone();
        phone phone1 = new phone();
        //创建线程A,B
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"A").start();
        //线程睡眠
        TimeUnit.SECONDS.sleep(1);

        new  Thread(()->{phone1.call();},"B").start();

    }


}
//电话类
class phone{
    //发短信得方法
    public static synchronized  void send() throws Exception {
        System.out.println("我是发短信得功能");
        //线程睡眠
        TimeUnit.SECONDS.sleep(4);
    }

    //打电话
    public  synchronized void call(){
        System.out.println("打电话");
    }
}

集合不安全类
并发情况下得Arrlist 安全吗?案例

package com.jj.demo.listdemo;

import java.util.ArrayList;
import java.util.UUID;

/**
 * @author fjj
 * @date 2021/3/20 15:51
 */
public class ListDemo {
    public static void main(String[] args) {
        //创建集合类
        ArrayList<String> list = new ArrayList<>();
        //循环放入数据
        for (int i = 1; i <20 ; i++) {
            //开启多线程
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },"A").start();
        }
    }
}

结果
在这里插入图片描述
多线程下得集合不安全。
解决方法一:。会想到使用vector<>()。我今天在看源码得时候才发现原来vector 要比Arrlist 先出来
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.jj.demo.listdemo;

        import java.util.ArrayList;
        import java.util.UUID;
        import java.util.Vector;

/**
 * @author fjj
 * @date 2021/3/20 15:51
 */
public class ListDemo {
    public static void main(String[] args) {
        //创建集合类
        Vector<String> list = new Vector<>();
        //循环放入数据
        for (int i = 1; i <20 ; i++) {
            //开启多线程
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },"A").start();
        }
    }
}

用这个集合类解决了并发情况下得安全问题。没有异常。不过,因为是 被synchronized 修饰。效率可能就不快了

解决方法二: 使用工具类来解决并发不安全得问题

package com.jj.demo.listdemo;

        import java.util.*;

/**
 * @author fjj
 * @date 2021/3/20 15:51
 */
public class ListDemo {
    public static void main(String[] args) {
        //创建集合类
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        //循环放入数据
        for (int i = 1; i <20 ; i++) {
            //开启多线程
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },"A").start();
        }
    }
}

通过工具类来加上同步代码块。效率还是不高。

解决方法四: 我们可以用 CopyOnWrite 写入时复制。COW 计算机程序设计得一种优化策略;多个线程
调用得时候,List 读取的时候,固定得,写入(覆盖),再写入得时候避免覆盖,造成数据问题!

package com.jj.demo.listdemo;

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

/**
 * @author fjj
 * @date 2021/3/20 15:51
 */
public class ListDemo {
    public static void main(String[] args) {
        //创建集合类
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        //循环放入数据
        for (int i = 1; i <20 ; i++) {
            //开启多线程
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },"A").start();
        }
    }
}

他得add 方法得源码
在这里插入图片描述
Set 集合不安全

package com.jj.demo.Setdemo;

import java.util.HashSet;
import java.util.UUID;

/**
 * @author fjj
 * @date 2021/3/20 16:37
 */
public class SetDemo {
    public static void main(String[] args) {
        //创建Set 集合
        HashSet<Object> set = new HashSet<>();
        for (int i = 1; i <20 ; i++) {
            //开启多线程
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },"A").start();
        }
    }
}

与上面一样,解决方法我们可以用CopyOnWrite

package com.jj.demo.Setdemo;

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

/**
 * @author fjj
 * @date 2021/3/20 16:37
 */
public class SetDemo {
    public static void main(String[] args) {
        //创建Set 集合
        CopyOnWriteArraySet<Object> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <20 ; i++) {
            //开启多线程
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },"A").start();
        }
    }
}

HahSet 得底层是Hash Map
在这里插入图片描述
Map 集合得使用

package com.jj.demo.HahMap;

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

/**
 * @author fjj
 * @date 2021/3/20 16:53
 */
public class HashMap {
    public static void main(String[] args) {
        //创建线程
        ConcurrentHashMap<Object, Object> hashMap = new ConcurrentHashMap<>();
        for (int i = 1; i < 400; i++) {
            new Thread(()->{
                hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(hashMap);
            },"A").start();
        }
    }
}

ConcurrentHashMap实现得原理
1)ConcurrentHashMap使用分段技术,将数据分为一段一段得存储。给每一段数据配置一把锁,当一个锁占用锁访问其中一段数据时,其他段得数据也能被其他线程访问了。
2)ConcurrentHashMap 是一个线程安全得哈希表,她得主要功能是提供一组和HashMap 功能相同但是线程安全得方法。
3)工作原理:
ConcurrentHashMap为了提高本身得并发能力,在内部采用了一个叫做Segment 得结构,一个Segment 其实就是一个类Hash Table 得结构,Segment 内部维护一个链表数组。

Callable 类得使用

package com.jj.demo.Callable;

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

/**
 * @author fjj
 * @date 2021/3/20 17:38
 */
public class CallableDemo {
    //获取 Callable 线程
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable1 callable1 = new Callable1();
        //获取适配器
        FutureTask task = new FutureTask(callable1);
        //获取线程
        new Thread(task,"A").start();
        //获取返回值
        Object o = task.get();
        System.out.println("o = " + o);
    }
}
class Callable1 implements Callable<Integer> {


    @Override
    public Integer call() throws Exception {
        System.out.println("我是call 方法");

        return 1111;
    }
}
相当于找了个中间商
结果
"C:\Program Files\Java\jdk1.8.0_231\bin\java.exe" "-javaagent:D:\idea\IntelliJ IDEA 2019.3.5\lib\idea_rt.jar=55948:D:\idea\IntelliJ IDEA 2019.3.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_231\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\rt.jar;E:\实习的代码\Demo\target\classes;E:\maven_jar包\org\openjdk\jmh\jmh-core\1.23\jmh-core-1.23.jar;E:\maven_jar包\net\sf\jopt-simple\jopt-simple\4.6\jopt-simple-4.6.jar;E:\maven_jar包\org\apache\commons\commons-math3\3.2\commons-math3-3.2.jar" com.jj.demo.Callable.CallableDemo
我是call 方法
o = 1111

Process finished with exit code 0

**JUC 的常用辅助类 **
CountDownLatch

package com.jj.demo.Callable;

import java.util.concurrent.CountDownLatch;

/**
 * @author fjj
 * @date 2021/3/20 19:56
 */
//计数器类
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
      //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i < 7; i++) {
            new Thread(()->{

                System.out.println(Thread.currentThread().getName());
                //数量减少1
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等待计数器归零,然后再向下执行
        countDownLatch.await();
        System.out.println("结束");
    }
}

CyclicBarrier 类

package com.jj.demo.Callable;

import java.util.concurrent.CyclicBarrier;

/**
 * @author fjj
 * @date 2021/3/20 20:01
 */
//数量增加
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {

            System.out.println("成功!");

        });
        for (int i = 1; i < 7; i++) {
            final int tem =i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+tem);
            }).start();
        }
    }
}

Semaphore 类

package com.jj.demo.Callable;

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

/**
 * @author fjj
 * @date 2021/3/20 20:21
 */
public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i =1; i <6 ; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"拿到了");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

ReadWriteLock 的案例

package com.jj.demo.ReadWriteLock;


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

/**
 * @author fjj
 * @date 2021/3/20 20:42
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        //获取缓存类
        Cache cache = new Cache();
        //开启写多线程
        for (int i = 1; i < 5; i++) {
            final int tem=i;
            new Thread(()->{
            cache.writ(tem,tem);
            },String.valueOf(i)).start();
        }
        //开启读多线程
        for (int i = 1; i < 5; i++) {
            final int tem=i;
            new Thread(()->{
             cache.read(tem);
            },String.valueOf(i)).start();
        }
    }
}
//自定义缓存类
class Cache{
    //定义一个Map
      private volatile Map<Integer,Object> map= new HashMap<>();
      //定义一个读写锁
      ReadWriteLock lock = new ReentrantReadWriteLock();

      //写的方法

    public void writ(Integer key,Object value){
        //加锁
        lock.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 {
            //释放锁
            lock.writeLock().unlock();
        }

    }
      //读的方法
    public void read(Integer key){
        //加锁
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.readLock().unlock();
        }
    }

}

结果
在这里插入图片描述

BlockingQueue 四组API
第一组:抛出异常的 ,当我们的队列满了之后抛出异常

package com.jj.demo.mq;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;

/**
 * @author fjj
 * @date 2021/3/21 15:54
 */
public class Demo1 {
    public static void main(String[] args) {
        test1();
    }
public static void test1(){
        //创建队列
    ArrayBlockingQueue deque = new ArrayBlockingQueue<>(3);
    //添加数据
    System.out.println(deque.add("a"));
    System.out.println(deque.add("b"));
    System.out.println(deque.add("c"));
 //检查队首元素
    System.out.println(deque.element());

    //读取数据
    System.out.println(deque.remove());
    System.out.println(deque.remove());
    System.out.println(deque.remove());



}

}

上面我们代码如果超过我们的队列就会抛出来异常。很不友好

方法二,我们可以用有返回值的参数

public static void test2(){
    ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
    //添加元素
    System.out.println(queue.offer("1"));
    System.out.println(queue.offer("2"));
    System.out.println(queue.offer("3"));
    System.out.println(queue.offer("4"));
    //检查首位元素
    System.out.println(queue.peek());
    //取出元素
    System.out.println(queue.poll());

}

**方法三 阻塞状态 **

public static void test3() throws InterruptedException {
        //获取队列
    ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
    //添加
    queue.put("A");
    queue.put("B");
    queue.put("C");
    //取出
    System.out.println(queue.take());
}

方法四:超时等待状态

public static void test4() throws InterruptedException {
        //创建队列
    ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
    //添加
    System.out.println(queue.offer("a"));
    System.out.println(queue.offer("a"));
    System.out.println(queue.offer("a"));
    System.out.println(queue.offer("a",2, TimeUnit.SECONDS));

}

超过2s 就不会在添加,就不会傻等了。
线程池
线程池:三大方法,7大参数,4种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!,优化资源的使用
线程池,连接池,内存池,对象池///… 创建,销毁,十分浪费资源
池化技术:事先准备好一些资源,有人要用,就来拿,用完还回去

线程池的好处
1,降低资源的消耗
2,提高响应的速度
3,方便管理
线程复用,可以控制最大的并发数,管理线程

线程的三大方法

package com.jj.demo.mq;

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

/**
 * @author fjj
 * @date 2021/3/21 17:32
 */
public class Demo2 {
    public static void main(String[] args) {
        //方法一,单个线程
        ExecutorService executor = Executors.newSingleThreadExecutor();
        //方法二,创建一个固定的线程池的大小
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //方法三 可伸缩,遇强则强,
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,使用线程池来创建线程
                executor.execute(()->{

                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池使用完毕后关闭
            executor.shutdown();
        }
    }
}

自定义线程池和四种拒绝策略
一般情况不能直接用工具类做线程池,我们应该用他底层的实现

package com.jj.demo.mq;

import java.util.concurrent.*;

/**
 * @author fjj
 * @date 2021/3/21 17:32
 */
public class Demo2 {
    public static void main(String[] args) {
   //自定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()

        );

        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,使用线程池来创建线程
                executor.execute(()->{

                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池使用完毕后关闭
            executor.shutdown();
        }
    }
}

在这里插入图片描述
四种拒绝策略

 */
// new ThreadPoolExecutor.AbortPolicy()  如果现在业务满了,还有人过来。抛出异常
//     new ThreadPoolExecutor.CallerRunsPolicy()  //哪里来的回到那里去
//    ThreadPoolExecutor.DiscardPolicy()  //队列满了,丢掉任务,不会抛出异常
    //new ThreadPoolExecutor.DiscardOldestPolicy()  队列满了,尝试跟最早的竞争,也不会抛出异常

版权声明:本文为m0_46937429原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。