JUC概述----2

目录

2.3ExecutorService ---执行器服务

 2.4lock -- 锁

 2.5 atomic 原子性


2.3ExecutorService ---执行器服务

线程池:访问服务端会产生大量的连接和销毁,避免对资源的浪费---减少连接和销毁的过程  ,做到资源的重用,就是存储线程资源的队列

连接池:访问数据库会有大量的连接和销毁,避免对资源的浪费---减少连接和销毁的过程,做到资源的重用

  • 每过来一个请求,都会创建一个线程去处理这个请求,直到线程数达到指定的数量不在创建为止,这些线程称之为核心线程

  • 在核心线程数量达到指定数量之前,每次请求都会创建新的
  • 核心线程使用完成之后不会被销毁,而是等待下一个任务
  • 如果核心线程成都被占用,则后续的请求会被放到一个工作队列中(阻塞式队列)
  • 如果此时临时线程也全部被占用,则后续的请求就会交给拒绝执行处理器来进行拒绝处理
  • 如果工作队列和核心线程资源都被占用,新来的请求会被交给临时线程处理---与核心线程唯一的区别就是用完就销毁
  • 临时线程使用完毕之后,不会被立即销毁,而是会存活一段时间,如果该时间段没有请求,就会被销毁

import java.util.concurrent.*;
public class ExecutorServiceDemo01 {
    public static void main(String[] args) {
        //拒绝执行处理器
        ExecutorService es = new ThreadPoolExecutor(
                5,//指定的核心线程数
                10,//最大线程数 = 核心线程数 + 临时线程数
                5,//临时线程存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<Runnable>(5),//工作队列
                (r, executor) -> System.out.println(r+"线程被拒绝"));
        //new Thread(new EsRunnable()).start();
        //执行线程
        for (int i = 0; i<18;i++){
            es.execute(new EsRunnable());//
        }
        //后续处理过程中若线程池长时间不使用,最好关闭
        //关闭线程池操作,不会被立即关闭,而是等待线程池中的线程全部完成工作为止
        es.shutdown();//实际开发中,基本不关
    }
}
class EsRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello~~");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

import java.util.concurrent.*;
public class ExecutorServiceDemo02 {
    public static void main(String[] args) {
        /**
         *    public static ExecutorService newCachedThreadPool() {
         *         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
         *                                       60L, TimeUnit.SECONDS,
         *                                       new SynchronousQueue<Runnable>());
         *     }
         *
         *     特点:
         *     1.没有核心线程,全部是临时线程
         *     2.临时线程的数量为Integer.MAX_VALUE
         *       人为理解这个线程池的容量大小为无界限
         *     3.临时线程存活时间是1min
         *     4.工作队列是一个同步式阻塞式队列,只能存储---1个元素
         *     大池子 小队列
         *     使用场景:
         *     适用于高并发任务的场景
         */
        ExecutorService es01  = Executors.newCachedThreadPool();
        /**
         * public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
         *         return new ThreadPoolExecutor(nThreads, nThreads,
         *                                       0L, TimeUnit.MILLISECONDS,
         *                                       new LinkedBlockingQueue<Runnable>(),
         *                                       threadFactory);
         *     }
         *
         *     特点:
         *     1.没有临时线程,全部是核心线程
         *     2.工作队列是一个链式阻塞式队列,默认容量大小为:Integer.MAX_VALUE
         *       人为认为该队列是无界限的
         *     使用场景:
         *     适用于长任务场景:比如 云盘 取消了不可以在恢复
         */
        ExecutorService es02 = Executors.newFixedThreadPool(5);
    }
}
C allable(处理多线程)
执行完毕之后,必须得有返回结果,所以泛型限定结果类型
R unnable和callable区别:
返回值:runnable没有返回值,callable有返回值
启动方式:
runnable基于thread启动也可以线程池启动,callable只能基于线程池启动
容错机制:runnable不允许抛出异常,因此不能以全局的方式进行处理(例如:spring中的异常处理通知);但是callable 允许以全局的形式处理 分叉合并线程池, 定时执行器服务
import java.util.concurrent.*;
public class CallableDemo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread().start();
        ExecutorService es = Executors.newCachedThreadPool();
        Future<String> f = es.submit(new CDemo());
        System.out.println(f.get());
        //既可以执行runnable,也可以执行callable
        //es.submit(new CDemo());
    }
}
class CDemo implements Callable<String>{
    @Override
    public String call() throws Exception {
        return null;
    }
}

 2.4lock -- 锁

lock是jdk1.5提供的一套用于取代 S ynch on ized机制,相对来说lock使用更加的简单灵活
public class LockDemo {
    static int i = 0;
    /**
     * main函数所在的类本身就是一个线程类
     * 那么这个线程类就是一个主线程,但是在执行过程中发现启动2个线程资源Add
     * 当调用start方法的时候会去启动Add资源,不会一调用start(),Add就会启动
     * 而是Add需要经历一系列的启动流程
     * 当Add线程启动过程中,主线程(LockDemo)会继续的抢占执行权继续向下执行
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Add()).start();//若是现在线程1会出现并发安全问题吗---不会
        new Thread(new Add()).start();//2个线程呢

        Thread.sleep(5000);

        System.out.println(i);//0 //174150 //200000
    }
}
class Add implements Runnable{
    @Override
    public void run() {
        /**
         * 若使用同步锁, 需要考虑锁对象的问题
         * 锁对象怎么确定??? 1.是对象  2.必须得是共享资源  目前变量i符合共享资源条件
         *
         * 另一个就是可以考虑当前类的字节码作为锁对象
         *
         * 会出现的问题:
         * 1.锁对象不好确定,如果确定错误,可能会出现当前锁无效
         *   严重会出现死锁或者大面积的互斥
         * */
        synchronized (Add.class) {
            for (int i = 0; i < 100000; i++) {
                LockDemo.i++;
            }
        }

    }
}

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo01 {
    static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock(true);//重入锁
        new Thread(new Add01(lock)).start();//若是现在线程1会出现并发安全问题吗---不会
        new Thread(new Add01(lock)).start();//2个线程呢
        Thread.sleep(5000);
        System.out.println(i);//0 //174150 //200000
    }
}
class Add01 implements Runnable{
    private final Lock lock;
    public Add01(Lock lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
         //加锁
         lock.lock();
         for (int i = 0; i < 100000; i++) {
           LockDemo01.i++;
         }
         //解锁
         lock.unlock();
    }
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo01 {
    static int i = 1;
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock(true);//重入锁
        new Thread(new Add01(lock)).start();//若是现在线程1会出现并发安全问题吗---不会
        new Thread(new Add01(lock)).start();//2个线程呢
        Thread.sleep(5000);
        System.out.println(i);//0 //174150 //200000
    }
}
class Add01 implements Runnable{
    private final Lock lock;
    public Add01(Lock lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
         //枷锁
         lock.lock();
         for (int i = 0; i < 100000; i++) {
           LockDemo01.i++;
         }
         //解锁
         lock.unlock();
    }
}
重入锁和非重入锁
重入锁:就是对象被释放,锁资源可以被再次的利用
非重入锁:就是对象被释放,锁资源不可以被再次的利用
注意:在原生的jdk中没有提供非重入锁(比如验证码或者网站发送的验证链接)
互斥锁和排他锁
实际开发中,绝大多数的锁都是排他锁 / 互斥锁---如果一个线程占用锁,其他的线程不能使用,处于阻塞状态
在互斥锁中,有一类锁非常的特殊,自旋锁,当一个线程抢占资源,其他线程不会被阻塞,而是会持续判断这个所资源是否被释
放 比较而言,自旋锁效率更高,优点在于线程状态的切换,但是会占用更多的CPU 资源
公平锁和非公平锁
非公平:抢占的是执行权
公平:抢占的是执行顺序 ( 会自带一个线程池---队列 )
Lock lock = new ReentrantLock (true);//引入公平策略
默认情况下使用的是非公平,因为公平策略需要考虑线程调度的问题,因此非公平效率更高

 其他

countdownlatch---闭锁/线程递减锁
用于线程计数,当达到制定的计数的时候,会放开阻塞允许后续的线程执行
考试:监考老师 考生

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo01 {

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

        CountDownLatch cdl = new CountDownLatch(6);
        new Thread(new teacher(cdl)).start();
        new Thread(new stu(cdl)).start();
        new Thread(new stu(cdl)).start();
        new Thread(new stu(cdl)).start();
        new Thread(new stu(cdl)).start();
        new Thread(new stu(cdl)).start();
        //在计数结束之前,就需要陷入阻塞
        cdl.await();
        System.out.println("开始考试~~~");

    }

}

class teacher implements Runnable{
    private CountDownLatch cdl;
    public teacher(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {
        System.out.println("监考老师到达考场~~~");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //减少一个线程
        cdl.countDown();
    }
}


class stu implements Runnable{

    private CountDownLatch cdl;

    public stu(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {
        System.out.println("考生到达考场~~~");

        cdl.countDown();
    }
}
CyclicBarrier --- 栅栏 也是用来线程计数
public class CyclicBarrierDemo01 {

    public static void main(String[] args) {

        CyclicBarrier cb = new CyclicBarrier(4);
        new Thread(new Runner(cb),"1号").start();
        new Thread(new Runner(cb),"2号").start();
        new Thread(new Runner(cb),"3号").start();
        new Thread(new Runner(cb),"4号").start();

    }
}

class Runner implements Runnable{

    private CyclicBarrier cb;

    public Runner(CyclicBarrier cb) {
        this.cb = cb;
    }

    @Override
    public void run() {


        try {
            //每一个运动员先集合
            String name = Thread.currentThread().getName();
            Thread.sleep((long) (Math.random()*10000));
            System.out.println(name+"来到起跑线~~~");
            //让当前的线程陷入阻塞,并且减少一个计数
            cb.await();
            System.out.println(name+"跑了出去~~~");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Semaphore --- 信号量 计数
如果有空余的信号,则线程可以获取信号进行执行,反之,线程就会被阻塞,实际开发中往往用于限流
public class SemaphoreDemo01 {

    public static void main(String[] args) {

        //创建信号量
        Semaphore s = new Semaphore(5);

        for (int i = 0; i<8; i++){

            new Thread(new Table(s)).start();

        }


    }

}

class Table implements Runnable{

    private Semaphore s;

    public Table(Semaphore s) {
        this.s = s;
    }

    @Override
    public void run() {

        try {
            //获取信号
            s.acquire();
            System.out.println("一张餐桌被占用~~~");
            //吃饭时间不固定
            Thread.sleep((long)(Math.random())*10000);
            //释放信号
            s.release();
            System.out.println("一张餐桌被释放~~~");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
E xchanger---交换机 用于线程间信息的交换
import java.util.concurrent.Exchanger;

public class ExchangerDemo01 {

    public static void main(String[] args) {

        //创建交换机
        Exchanger<String> ex = new Exchanger();

        //启动线程
        new Thread(new Product(ex)).start();
        new Thread(new Consumer(ex)).start();
    }
}

//商家
class Product implements Runnable{


    //接收一个交换机进行交换
    private Exchanger<String> ex;

    public Product(Exchanger<String> ex) {
        this.ex = ex;
    }

    @Override
    public void run() {
        try {
            //从另一端获取交换信息
            String info = ex.exchange("商品");
            System.out.println("商家收到了消费者交换的:"+info);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//消费者
class Consumer implements Runnable{

    //接收一个交换机进行交换
    private Exchanger<String> ex;

    public Consumer(Exchanger<String> ex) {
        this.ex = ex;
    }

    @Override
    public void run() {
        try {
            //从另一端获取交换信息
            String info = ex.exchange("钱");
            System.out.println("消费者收到了商家交换的:"+info);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 2.5 atomic 原子性

原子性底层就是volatile+CAS
在原子性操作中,提供了大量的线程安全的方法,在早期的时候,利用volatile+锁机制来保证线程安全,在jdk1.8开始,用的是 volatil+CAS来保证线程安全。
volatile是java中关键字之一,是JDK提供的轻量级的用于线程安全的可以进行通信的机制
     保证可见性,当一个线程对共享资源进行修改的时候,需要让其他的线程感知到共享资源发        生变化    如何感知,就是将共享资源可见---添加volatile关键字进行修饰
     不能保证原子性:添加volatile不能保证; 线程在执行过程中不可分割,线程执行过程中不能     被打断或者发生资源的抢占; 加锁就是为了保证线程的原子性,但是即便添加了volatile,依然       会发生线程安全问题
    volatile(保证数据可见)+CAS(保证线程安全)
public class VolatileDemo01 {
    public static void main(String[] args) throws Exception {
        Data01 d = new Data01();
        d.i = 5;
        new Thread(()->{
            System.out.println("线程A启动");
            while (d.i ==5);
            System.out.println("线程A结束");
        }).start();
        //为了给线程A启动的时间,延迟线程B的启动
        Thread.sleep(3000);
        new Thread(()->{
            System.out.println("线程B启动");
            d.i = 7;
            System.out.println("线程B结束");
        }).start();
    }
}
class Data01{
    volatile int i;
}

 

import java.util.concurrent.CountDownLatch;
public class VolatileDemo02 {
    public static void main(String[] args) throws Exception {
        CountDownLatch cdl = new CountDownLatch(2);
        Data01 d = new Data01();
        new Thread(new Sum(d,cdl)).start();
        new Thread(new Sum(d,cdl)).start();
        cdl.await();
        System.out.println(d.i);//174414
    }
}
class Sum implements Runnable{
    private Data01 d;
    private CountDownLatch cdl;
    public Sum(Data01 d, CountDownLatch cdl) {
        this.d = d;
        this.cdl = cdl;
    }
    @Override
    public void run() {
        for (int i = 0; i<100000; i++){
            d.i++;
        }
        cdl.countDown();
    }
}

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicDemo01 {
    //static int i= 0;
    static AtomicInteger ai = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch cdl = new CountDownLatch(2);
        new Thread(new Add02(cdl)).start();
        new Thread(new Add02(cdl)).start();
        cdl.await();
        System.out.println(ai);
    }
}
class Add02 implements Runnable{
    private CountDownLatch cdl;
    public Add02(CountDownLatch cdl) {
        this.cdl = cdl;
    }
    @Override
    public void run() {
            for (int i = 0; i < 100000; i++) {
                //AtomicDemo01.i++;
                AtomicDemo01.ai.incrementAndGet();
            }
            cdl.countDown();
        }
}

 

    禁止指令重排:指令重拍就是指令的执行顺序和定义顺序不一样
   指令重拍本质上一种优化,只不过这种优化起到的是反作用;程序在转换为计算机指令以及执行     的过程中,有不到百分之一 的可能会会发生重排,但是随着性能的提升,重排发生的概率会降低
   指令无论怎么重新排序,必须遵循‘happen-before 原则(优先发生原则)’---后边使用的东西,前     面必须存在
   编译---》class文件--》
   按照jvm规范将程序 映射 成通用文件格式
   指令重排,可能会发生在以下各个阶段:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值