Java多线程深入-----JUC详解

多线程进阶----JUC并发编程详解

1、什么是JUC

1、java.util工具包、包、分类

业务:普通的线程代码 Thread

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

2、线程和进程

1、进程:一个程序,程序的集合;

  • 一个进程往往可以包含多个线程,至少包含一个
  • Java默认有2个线程:main、GC

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

对于Java而言,开启线程的三种方式:Thread、Runnable、Callable

  • Java无法开启线程:
//本地方法,底层的C++,Java无法直接操作硬件
private native void start();

3、并发和并行

  • 并发编程:多线程操作同一个资源。CPU一核,模拟出多天线程,快速交替
  • 并行:多个人一起行走。CPU多核,多个线程可以同时执行;线程池
package JUC_Study;

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


    }
}

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

4、线程的状态:新生、运行、阻塞、等待、超时等待、终止

5、wait和sleep的区别

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

3、Lock(锁)

1、传统Synchronized

package JUC_Study;
//基本的卖票例子。真正的多线程开发,公司中的开发,降低耦合性
public class SaleTicketDemo01 {
    //线程就是一个单独的资源类,没有任何附属的操作

    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入线程
        Ticket ticket = new Ticket();
        //lambda表达式:()->{代码}
        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 number = 50;
    private int i = 1;
    //卖票的方法
    public synchronized void sale(){
        if(number>0){
            number--;

            System.out.println(Thread.currentThread().getName()+"卖出了"+i+"票,剩余:"+number);
            i++;
        }

    }


}

2、JUC中LOCK接口

  • Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); } 
    
  • 所有已知实现类:

    ReentrantLock , ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

  • 公平锁:十分公平,必须先来后到

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

package JUC_Study;

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

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入线程
        Ticket2 ticket = new Ticket2();
        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
//Lock三部曲
//1.new ReentrantLock()  2.加锁  lock.lock();  3.解锁: finally ==>lock.unlock()
class Ticket2{
    private int number = 40;
    Lock lock = new ReentrantLock();
    public void sale(){
        lock.lock();

        try {
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }







}




  • Lock三部曲:
    1.new ReentrantLock() 2.加锁 lock.lock(); 3.解锁: finally ==>lock.unlock()

3、Synchroized和Lock的区别

  • Synchroized :内置的java关键字,Lock是一个java类
  • Synchroized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
  • Synchroized会自动释放锁,Lock必须手动释放解锁,如果不释放锁,–死锁
  • Synchroized:线程1(获得锁)、线程2(等待),如果线程一阻塞,线程二也会一直等;Lock锁则不会一直等待 lock.tryLock()
  • Synchroized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)
  • Synchroized 适合锁少量的代码同步问题, Lock适合锁大量的同步代码

4、生产者和消费者问题

1.判断是否等待,2.业务代码,3.通知下一个线程

1、Synchroized 版 wait notify

package JUC_Study.pc;
/*
* 线程之间的通信问题:生产者和消费者问题  等待唤醒,  通知唤醒
* 线程交替执行   A  B操作同一个变量  num=0
* A  num+1
* B  num-1
* 
* */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

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

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

    }
}
//判断等待,业务,通知
class Data{
    private int number = 0;
    //方法 +1
    public synchronized void increment() throws InterruptedException {
        while (number!=0){  //0才干活
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1完毕
        this.notifyAll();
    }

    //方法 -1
    public synchronized void decrement() throws InterruptedException {
        while (number==0){//1才干活,1-1=0
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我-1完毕
        this.notifyAll();
    }


}

  • 如果有多个abcd线程,会出现虚假唤醒----if判断改为while判断

2、juc lock版生产者和消费者问题

package JUC_Study.pc;

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

public class B  {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

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

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

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

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


    }
}
//判断等待,业务,通知
class Data2{
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //方法 +1
    public  void increment() throws InterruptedException {
        try {
            lock.lock();
            //业务代码
            while (number != 0) {  //0才干活
                //等待
                condition.await();

            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //通知其他线程,我+1完毕
            condition.signalAll();


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


    //方法 -1
    public  void decrement() throws InterruptedException {
            try {
                lock.lock();
                while (number==0){//1才干活,1-1=0
                    //等待
                    condition.await();

                }
                number--;
                System.out.println(Thread.currentThread().getName()+"=>"+number);
                //通知其他线程,我-1完毕
                condition.signalAll();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }


    }


// condition.await();等待
// condition.signalAll();唤醒



3、Condition 精准的通知和唤醒线程

package JUC_Study.pc;

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

/*
*
*
*
* */
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        //有序执行线程,A->B->C
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printA();

            }

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

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

        },"C").start();
    }
}
class Data3{//资源类
     private Lock lock = new ReentrantLock();
     private Condition condition1= lock.newCondition();
    private Condition condition2= lock.newCondition();
    private Condition condition3= lock.newCondition();
    private int number =1; //1A  2B  3C
     public  void  printA(){
         lock.lock();
         try {
             while (number != 1){
                 //等待
                 condition1.await();
             }
             System.out.println(Thread.currentThread().getName()+"=>AAAA");
             //唤醒指定人,B
             number = 2;
             condition2.signal();//唤醒2,就是唤醒B,精准唤醒


         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             lock.unlock();
         }

     }
    public void printB(){
        lock.lock();
        try {
            while (number!=2){
                //等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBBB");
            number = 3;
            condition3.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printC(){
        lock.lock();
        try {
            while (number!=3){
                //等待
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>CCCC");
            number = 1;
            condition1.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }


}

5、 八锁现象

深刻理解锁

1、代码测试:

package JUC_Study.Lock8;
/*
* 8锁就是关于锁的八个问题
* 1、标准情况下,两个线程先打印发短信还是打电话?  :先发短信再打电话
* 2、senSms 四秒延迟  :   依然是先发短信后打电话
* 原因:synchronized 锁的对象是方法的调用者,两个方法用的是同一个锁,谁先拿到谁执行
*
*
* */

import java.util.concurrent.TimeUnit;

public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.senSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

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

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

    }

}






package JUC_Study.Lock8;

import java.util.concurrent.TimeUnit;
/*
* 3、增加了一个普通方法后,是先执行发短信还是hello?
*不一定。普通方法不受锁的限制,CPU会判断间隔时间,如果判断执行hello线程的延时时间比发短信短,,那么就算先调用发短信,也还是先打电话。
* 如果延时时间一样,先执行有锁的。
*
*4、两个对象,两个同步方法:两把锁,按时间来,打电话延时小,所以先打电话
*
* */

public class Test2 {
    public static void main(String[] args) {
        //两个对象
        Phone2 phone2 = new Phone2();
        Phone2 phone1 = new Phone2();
        new Thread(()->{
            phone1.senSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

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

class Phone2{
    public synchronized void senSms(){
        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");
    }

}







package JUC_Study.Lock8;

import java.util.concurrent.TimeUnit;
/*5、 增加两个静态的同步方法,只有一个对象:先发短信再打电话。
static:静态方法,类一加载就有了!Class,Phone3唯一的一个class对象,锁的是Class

6、两个对象,增加两个静态的同步方法  :先发短信再打电话锁的是Class,两个对象的Class类模板只有一个。
*
*
*
* */
public class Test3 {
    public static void main(String[] args) {
        //两个对象
        Phone3 phone1= new Phone3();
        Phone3 phone2 = new Phone3();

        new Thread(()->{
            phone1.senSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

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

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

    }
    //没有锁,不受锁的影响
    public static void hello(){
        System.out.println("hello");
    }

}






package JUC_Study.Lock8;

import java.util.concurrent.TimeUnit;
/*
* 7、一个静态同步方法,一个普通同步方法,一个对象:先打电话(普通)后发短信(静态)
* 8、一个静态同步方法,一个普通同步方法,两个对象:先打电话(普通)后发短信(静态)
*
*
*
* */

public class Test4 {
    public static void main(String[] args) {
        //两个对象
        Phone4 phone1= new Phone4();
        Phone4 phone2= new Phone4();


        new Thread(()->{
            phone1.senSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

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

class Phone4{
    //静态同步方法
    public static synchronized void senSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通同步方法
    public  synchronized void call(){
        System.out.println("打电话");

    }
    //没有锁,不受锁的影响
    public static void hello(){
        System.out.println("hello");
    }

}



2、小结

  • new this 具体的一个手机,锁的是对象
  • static Class 唯一的模板,锁的是Class

6、集合类不安全

1、List不安全

java.util.ConcurrentModificationException 并发修改异常!

解决方案:

1):并发下,ArrayList不安全,把new ArrayList 换成 new Vector()

2): 用集合工具类,Listlist =Collections.synchronizedList(new ArrayList<>());

3):JUC里的类, Listlist =new CopyOnWriteArrayList<>();

CopyOnWrite:写入时赋值,COW 计算机程序设计领域的一种优化策略,多个线程调用时,list,读取的时候,固定的,写入(覆盖),在写入的时候避免覆盖,造成数据问题

package JUC_Study.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/*
*  java.util.ConcurrentModificationException  并发修改异常!
*一、解决方案:
* 1):并发下,ArrayList不安全,把new ArrayList 换成 new Vector()
* 2): 用集合工具类,Listlist =Collections.synchronizedList(new ArrayList<>());
* 3):JUC里的类, Listlist =new CopyOnWriteArrayList<>();
*二、CopyOnWrite:写入时赋值,COW  计算机程序设计领域的一种优化策略,多个线程调用时,list,读取的时候,固定的,写入(覆盖)
* 在写入的时候避免覆盖,造成数据问题
* 
*
* */

public class ListTest {
    public static void main(String[] args) {
        //并发下,ArrayList不安全
        List<String>list =new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 100; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();


        }
    }
}

2、Set不安全

package JUC_Study.unsafe;

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

/*
* java.util.ConcurrentModificationException  并发修改异常
* 解决方案:
* 1)Setset = Collections.synchronizedSet(new HashSet<>());
* 2) Setset = new CopyOnWriteArraySet<>();
*
*
*
* */
public class SetTest {
    public static void main(String[] args) {
        Set<String>set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();

        }
    }
}

HashSet底层是HashMap,add set 本质就是map key,是无法重复的

3、Map不安全

package JUC_Study.unsafe;

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

/*
* java.util.ConcurrentModificationException
* 解决方案:
* 1) Mapmap = new ConcurrentHashMap<>();
*
* */
public class MapTest {
    public static void main(String[] args) {
        //map
        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

1、可以有返回值

2、可以抛出异常

3、方法不同

package JUC_Study.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 {

        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);
        new Thread(futureTask,"A").start();
        String o = (String) futureTask.get();//返回值  这个get方法可能会产生阻塞,把它放到最后
        System.out.println(o);




    }
}
class MyThread implements Callable<String>{
    @Override
    public String call() throws Exception {
        System.out.println("call()");
        return "45678";
    }
}

细节:

  • 有缓存
  • 结果可能需要等待,会阻塞

8、常用的辅助类

1)CountDownLatch

减法计数器

package JUC_Study.add;

import java.util.concurrent.CountDownLatch;

// 计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"Go Out");
                countDownLatch.countDown();
            },String.valueOf(i)).start();

        }
        countDownLatch.await();//等待计数器归零,然后再向下执行
        System.out.println("close door");

    }
}

2)CyclicBarrier

这是一个加法计数器

package JUC_Study.add;

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

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功!");//等待收集到7,才输出召唤成功
        });
        for (int i = 1; i <= 7; i++) {
            final int temp = i;//lambda表达式拿不到i,必须通过final
            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

抢车位,限流也可以用

package JUC_Study.add;

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

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

    }
}

JUC并发编程(二)

9、读写锁

ReadWriteLock :读可以被多线程同时读,写的时候只能有一个线程写

代码测试:

package JUC_Study.RW;

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

/*
* ReadWriteLock
*
*
*
* */
public class 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<>();
    //new一个读写锁,更加细粒度的控制
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    //存,写,写入的时候只希望同时只有一个线程写
    public void put(String 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 get(String key){
        lock.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 {
            lock.readLock().unlock();
        }

    }
}
  • 读-读:可以共存
  • 读-写:不能共存
  • 写-写:不能共存
  • 独占锁(写锁,一次只能被一个线程占有),共享锁(读锁,多个线程可以同时占用)

10、阻塞队列(BlockingQueue)

1、不得不阻塞

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

取:如果队列是空的,必须阻塞等待生产

2、BlockingQueue不是新东西。什么情况下使用阻塞队列?

  • 多线程并发处理、线程池

3、学会使用队列

添加、移除

四组API

  • 抛出异常
package JUC_Study.BQ;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main(String[] args) {
        test1();

    }

    //抛出异常
    public static void test1(){
        //队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //已经满了,抛出异常:IllegalStateException: Queue full,队列已满
        //System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());//按顺序移除,a,b,c
        //如果没有元素,抛出异常:java.util.NoSuchElementException 
    }
}

  • 有返回值,不会抛出异常
public static void test2(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        //满了不抛出异常,返回一个false
        System.out.println(blockingQueue.offer("d"));
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//null,也不会抛出异常


    }
//检测队首元素,返回队首元素
blockingQueue.peek()
blockingQueue.element()

  • 阻塞等待(一直阻塞)
public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //队列没有位置,一直阻塞,一直等待(程序不结束)
        //blockingQueue.put("d");
        System.out.println(blockingQueue.take());//a
        

    }
  • 超时等待(等待超时退出)
 public static void test4() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        blockingQueue.offer("d", 2,TimeUnit.SECONDS);//等待超过2s退出
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
    }

4、 SynchronousQueue同步队列

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

put,take

package JUC_Study.BQ;

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

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

            try {
                System.out.println(Thread.currentThread().getName()+"put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

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

}
//输出结果:
T1put 1
T21
T1put 2
T22
T1put 3
T23
//进去一个元素后,只有取出来才能再往里放。SynchronousQueue不能存储

11、线程池(三大方法,七种参数,四种拒绝策略)

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

如:线程池、连接池、内存池、对象池,创建和销毁十分浪费资源

2、 池化技术:实现准备好一些资源,有人要用,就来这里拿,用完之后还给我

3、 线程池的好处:

  • 降低资源的消耗
  • 提高相应的速度
  • 方便管理
  • 线程复用,可以控制对打并发数,管理线程

4、线程池三大方法

  • Executors.newSingleThreadExecutor(); 单个线程
 public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();

        //线程池用完,程序结束,关闭线程池

        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
  • ExecutorService threadPool = Executors.newFixedThreadPool(5);固定线程数的线程池,永远只有五个
  • Executors.newCachedThreadPool(); 可伸缩的线程池

5、 线程池七大参数

  • int corePoolSize:核心线程大小
  • int maximumPoolSize:最大核心线程池大小
  • long keepAliveTime:超时了没有人调用就会释放
  • TimeUnit unit : 超时单位
  • BlockingQueueworkQueue :阻塞队列
  • ThreadFactory threadFactory :线程工厂,创建线程的,一般不用动
  • RejectedExecutionHandler handle :拒绝策略

核心线程数:就是一直有的,最小线程数。

6、自定线程池

package JUC_Study.pool;

import java.util.concurrent.*;

public class Demo02 {
    public static void main(String[] args) {
        //自定义线程池,七大参数
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),//阻塞队列
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略的一种,银行满了,还有人进来,就不处理这个人并抛出异常

        try {
            //最大承载:阻塞队列+最大核心线程数
            for (int i = 0; i < 9; i++) {
                //使用了线程池之后,用线程池来创建线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }
    }
}

7、四种拒绝策略

  • new ThreadPoolExecutor.AbortPolicy();//银行满了,还有人进来,就不处理这个人并抛出异常
  • new ThreadPoolExecutor.CallerRunsPolicy();//哪来的去哪里,main线程处理,不抛出异常
  • new ThreadPoolExecutor.DiscardOldestPolicy();//队列满了,尝试丢掉最之前的那个人(抛弃最老的线程),不抛出异常
  • new ThreadPoolExecutor.DiscardPolicy();//银行满了,还有人进来,抛弃这个线程,不抛出异常

8、线程池的最大线程数应该该如何设置:

  • CPU密集型 : 几核就是最大几线程,可以保证CPU效率最高
System.out.println(Runtime.getRuntime().availableProcessors());//获取CPU核数
  • IO密集型 :判断程序中十分耗IO的线程数,只要大于这个线程数就好了(可以两倍)

12、 四大函数式接口

1、函数型接口:FunctionalInterface,简化编程模型

 public static void main(String[] args) {
        //工具类:输出输入的值
//       Function function = new Function() {
//           @Override
//           public String apply(String str) {
//               return str;
//           }
//       };
        //简化
       Function function =  (str)->{return str;};
        System.out.println(function.apply("xqh"));

    }

2、断定型接口(有一个输入参数,返回值只能是布尔值)

public static void main(String[] args) {
        //判断字符串是否为空
//        Predicate predicate = new Predicate() {
//            @Override
//            public boolean test(String str) {
//                return str.isEmpty();
//            }
//        };
        //简化
        Predicate<String> predicate=(str)->{return str.isEmpty();};
        System.out.println(predicate.test("x"));//false 不为空

    }

3、消费型接口

  • Consumer 消费型接口,只有输入,没有返回值
  public static void main(String[] args) {
//        Consumerconsumer = new Consumer() {
//            @Override
//            public void accept(String str) {
//                System.out.println(str);
//
//            }
//        };
//        consumer.accept("ass");
        //简化
        Consumer<String>consumer = (str)->{
            System.out.println(str);
        };
        consumer.accept("xqh");
    }

4、供给型接口

  • 供给型接口 Supplier 没有参数,只有返回值
 public static void main(String[] args) {
//        Supplier supplier = new Supplier() {
//            @Override
//            public Integer get() {
//                return 1024;
//            }
//        };
//        System.out.println(supplier.get());
        //简化
        Supplier supplier = ()->{return 1024;};
        System.out.println(supplier.get());



    }

13、 Stream流式计算

1、什么是 Stream流式计算?

  • 大数据:存储+计算
  • 集合、MySQL本质就是存储东西,计算都应该交给流来操作

2、代码测试

package JUC_Study.Stream;

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

/*
* 题目要求:
* 现在有5个用户,筛选:
* 1.ID必须是偶数
* 2.年龄必须大于23岁
* 3.用户名转为大写字母
* 4.用户名字母倒着排序
* 5.只输出一个用户
* */
public class Test01 {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 22);
        User u3 = new User(3, "c", 23);
        User u4 = new User(4, "d", 24);
        User u5 = new User(6, "e", 25);
        //集合就是存储
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        //计算交给Stream流
        list.stream()
                .filter(u -> {return u.getId() % 2 == 0;})
                .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,并行执行任务,提高效率,大数据量!
  • 大数据:Map Reduce (把大任务拆分为小)
  • 把大任务分解,结果再合并

2、ForkJoin特点:工作窃取

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

15、异步回调

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

package JUC_Study.future;

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

/*异步调用 CompletableFuture
*异步执行
* 成功回调
* 失败回调
*
* */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //发起一个请求
        //没有返回值的runAsync异步回调
        CompletableFuture<Void> completableFuture =  CompletableFuture.runAsync(()->{

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
        });
        System.out.println("1111");
        completableFuture.get();//阻塞获取执行结果

    }
}

JUC并发编程(三)

16、JMM

1、Volatile的理解

  • Volatile是Java虚拟机提供轻量级的同步机制
  • 保证可见性(和JMM挂钩)
  • 不保证原子性
  • 禁止指令重排

2、JMM

  • :Java内存模型,不存在的东西,是一个概念
  • 关于JMM的一些同步的约定
    • 线程解锁前,必须把共享变量立刻刷回主存
    • 线程加锁前,必须读取主存中的最新值到工作内存中!
    • 加锁和解锁是同一把锁

3、线程:工作内存、主内存

4、八种操作

load , read , use , assign , write , store , lock , unlock

17、Volatile

1、保证可见性

package JUC_Study.tvolatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    private  volatile static int num  =0;
    public static void main(String[] args) {//main线程


        new Thread(()->{//线程1
            while (num == 0){}
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
//输出了1,但是程序并没有停止
        //不加 volatile程序就会死循环,加了 volatile会停止,因为保证了可见性
    }
}

2、不保证原子性

原子性:ACID,不可分割

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

package JUC_Study.tvolatile;

public class Demo02 {
    private volatile static int num = 0;
    public   static void add(){
        num++;
    }

    public static void main(String[] args) {
        //理论上num结果应该为2万
        for(int i = 1;i<=20;i++){
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}
//加Synchronize可以,加volatile不行
  • 如果不加Synchronize,lock,如何保证原子性?

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

private volatile static AtomicInteger num = new AtomicInteger();
    public   static void add(){
        num.getAndIncrement();
    }

3、指令重排

  • :你写的程序,计算机并不是按照你写的那样去执行的
  • 源代码—>编译器优化的重排—>指令并行也可能会重排----->内存系统也会重排------>执行
  • 结果是你想要的结果,但是执行顺序计算机可能会重排
  • 处理器在进行指令重排的时候,会考虑:数据之间的依赖性

4、Volatile是可以保证可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!

18、单例模式

1、饿汉式

package JUC_Study.Singal;
//饿汉式单例
public class Hungry {
    //可能会浪费空间
    private byte[]data1 = new byte[1024*1024];
    private Hungry(){

    }
    private final static Hungry HUNGRY = new Hungry();
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

2、懒汉式

package JUC_Study.Singal;

public class lazy {
    private lazy(){
        System.out.println(Thread.currentThread().getName()+"ok");
        
    }
    //双重检测锁模式的 懒汉式单例 DCL懒汉式
    private static lazy lazy;
    public static lazy getInstance(){
        if(lazy == null){
            synchronized (lazy.class){
                if(lazy == null){
                    lazy = new lazy();
                }
            }
        }
        
        return lazy;
    }
    //多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                lazy.getInstance();
            }).start();
        }
    }

}

19、深入了解CAS

20、原子引用

21、各种锁的理解

1)公平锁、非公平锁

公平锁:非常公平,不能插队

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

Lock lock = new ReentrantLock();//默认是非公平锁

2)可重入锁

  • 所有的锁都是可重入锁
  • 也叫递归锁
  • 拿到了外面的锁,就可以拿到里面的锁,自动获得

3)自旋锁

  • 不断遍历迭代
  • spinlock
package JUC_Study.Lock;

import java.util.concurrent.atomic.AtomicReference;

//自旋锁
public class Demo01 {
    AtomicReference<Thread> atomicReference=new AtomicReference<>();
    
    
    //加锁
    public void myLock(){
        Thread thread =Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"==>myLock");
        //自旋锁
        while (atomicReference.compareAndSet(null,thread)){
            
        }
    }
    
    
    //解锁
    public void unLock(){
        Thread thread =Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"==>myUnLock");
         atomicReference.compareAndSet(thread,null);

        }
        
    }


4)死锁

  • 死锁:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。通俗点说:死锁就是两个线程同时占用两个资源,但又在彼此等待对方释放锁。

你可能感兴趣的:(javase,java)