【Java (一:10-2) 多线程学习】

本文介绍了Java中的多线程概念,包括线程状态、线程池的使用,如Executors.newCachedThreadPool()和newFixedThreadPool(),以及自定义线程池ThreadPoolExecutor的配置。讨论了线程池的任务拒绝策略,如AbortPolicy、DiscardPolicy等。此外,还讲解了volatile关键字的作用和synchronized与CAS的区别。最后,提到了并发工具类中的原子性操作和AtomicInteger的使用,以及并发安全的HashMap替代方案如Hashtable和ConcurrentHashMap。
摘要由CSDN通过智能技术生成

多线程学习

四、线程池&volatile

1. 线程状态

在这里插入图片描述
在这里插入图片描述

2. 线程池

线程池的原理类似于用碗吃饭,吃完后放回橱柜(就是线程池),如果这个碗正在使用(A线程),这时需要新的线程B执行,则从橱柜里重新拿碗,如果A线程使用的碗还未归还则需要拿新的碗进行吃饭;
在这里插入图片描述

3. 线程池-Executors

3.1 Executors.newCachedThreadPool();


//创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
//static ExecutorService newCachedThreadPool()
//创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
//static ExecutorService newFixedThreadPool(int nThreads)

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

public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        //创建一个默认线程池对象 默认是空的 默认最大是int的最大值
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors 帮助我们创建线程池对象
        //ExecutorService 帮追我们管理线程池
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
      //  Thread.sleep(2000);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        executorService.shutdown();


    }

}

此时之所以是两个线程执行因为线程1执行完还未归还线程池内时,线程2已经执行,所以时两个线程对象
执行结果
在这里插入图片描述

执行时后睡2s后执行,由一个线程对象执行的
在这里插入图片描述

3.2 Executors.newFixedThreadPool();

newFixedThreadPool(10) 这里的值 是线程池的最大值


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

//创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
//static ExecutorService newFixedThreadPool(int nThreads)
public class ThreadPool2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
         executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
         executorService.shutdown();
    }
}

//获取最大线程池容量

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

//创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
//static ExecutorService newFixedThreadPool(int nThreads)
public class ThreadPool2 {
    public static void main(String[] args) {

        //参数不是初始值 而是最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);

       ThreadPoolExecutor pool=(ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
         executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
         executorService.shutdown();
        System.out.println(pool.getPoolSize());
    }
}

4. 自定义线程池 -ThreadPoolExecutor

在这里插入图片描述
在这里插入图片描述
代码实现


//ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
// BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
//        创建一个新 ThreadPoolExecutor给定的初始参数。

import java.util.concurrent.*;

/***
 * 参数1:核心线程数量
 * 参数2:最大线程数
 * 参数3:空闲线程最大存活时间
 * 参数4:时间单位
 * 参数5:任务队列  如下最大执行为5个线程,超过5个线程时就要在队列中等待
 * 参数6:任务创建工厂 按照默认方式创建线程对象 源码中还是new Thread()
 * 参数7:任务拒绝策略  什么时候拒绝任务?  当提交任务>池子中最大线程数量+任务队列容量           怎样拒绝任务?? 
 * 4种拒绝任务策略
 */
public class ThreadPool3 {
    static class MyRunnable implements  Runnable{


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"在执行了");
        }
    }
    public static void main(String[] args) {


    ThreadPoolExecutor pool=new ThreadPoolExecutor(
            2,
            5,
            2,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
    pool.submit(new MyRunnable());
    pool.submit(new MyRunnable());
    pool.shutdown();
    }
}

4.1线程池任务拒绝策略

4.1.1 new ThreadPoolExecutor.AbortPolicy()

超过线程池中线程最大容量+任务队列容量时:丢弃任务并抛出异常

代码实现


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/***
 * 参数1:核心线程数量
 * 参数2:最大线程数
 * 参数3:空闲线程最大存活时间
 * 参数4:时间单位
 * 参数5:任务队列
 * 参数6:任务创建工厂
 * 参数7:任务拒绝策略
 */
public class ThreadPool4 {
    static class MyRunnable implements  Runnable{


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"在执行了");
        }
    }
    public static void main(String[] args) {


        ThreadPoolExecutor pool=new ThreadPoolExecutor(
                2,
                5,
                2,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 1; i <= 16; i++) {
            pool.submit(new MyRunnable());
        }


        pool.shutdown();
    }
}

4.1.2 new ThreadPoolExecutor.DiscardPolicy()

直接丢弃 不抛异常


public class ThreadPool4 {
    static class MyRunnable implements  Runnable{


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"在执行了");
        }
    }
    public static void main(String[] args) {


        ThreadPoolExecutor pool=new ThreadPoolExecutor(
                1,
                2,
                2,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());

        for (int i = 1; i <= 5; i++) {
            int y=i;
            pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"*****"+y);
            });
        }
        pool.shutdown();
    }
}

执行结果:
在这里插入图片描述

4.1.3 new ThreadPoolExecutor.DiscardOldestPolicy()

抛弃队列中等待最久,加入当前任务进入队列
直接放结果
在这里插入图片描述

4.1.4 new ThreadPoolExecutor.CallerRunsPolicy()

在这里插入图片描述

5. volatile关键字

强制线程每次使用的时候,都会看一下共享区域最新的值
对 volatile 修饰的变量值,保证线程读取到的值是最新的,而不是寄存器中缓存的值。
在这里插入图片描述
在这里插入图片描述


class Money {
    public  static volatile  int money=100000;

    public static void main(String[] args) {
        MyThead1 t1=new MyThead1();
        MyThead2 t2=new MyThead2();
        t2.start();
        t1.start();
    }
}
class MyThead2  extends  Thread{
@Override
public void run() {

        try {
        Thread.sleep(10);
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
        Money.money=90000;
        }
        }

public class MyThead1  extends  Thread{

    @Override
    public void run() {
        while (Money.money==100000){

        }
        System.out.println("结婚基金已经不是10万了");
    }
}

5.1 synchronized解决

代码实现:


class Money {
    public static volatile int money = 100000;
    public static Object lock = new Object();

    public static void main(String[] args) {
        MyThead1 t1 = new MyThead1();
        MyThead2 t2 = new MyThead2();
        t2.start();
        t1.start();
    }
}

class MyThead2 extends Thread {
    @Override
    public void run() {
        synchronized (Money.lock){
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Money.money = 90000;
        }
    }
}

public class MyThead1 extends Thread {

    @Override
    public void run() {
        while (true) {
            synchronized (Money.lock) {
                if (Money.money != 100000) {
                    System.out.println("结婚基金已经不是10万了");
                    break;
                }
            }
        }

    }
}

原理:
在这里插入图片描述

五、 原子性&&并发工具类

1. 原子性

多个操作是一个不可以分割的整体
在这里插入图片描述
volatile不支持原子性

/*
* 当线程A和线程B同时对共享数据操作时,可能A改变了线程栈中副本的值,但是未返回给共享数据时就被线程B
* 抢占了,这时B访问共享数据取到的还是原来的数据,但是当A,B返回共享数据时 实际的操作就出现了不合理
*  也就是 count++ 不是原子性操作
*/

public class MyAtomThread extends Thread {
    private  int volatile  count =0;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1. 从共享数据中读取数据到本线程栈中
            //2. 修改本线程栈中变量副本的值
            //3. 把本线程栈中变量副本的值赋值给共享数据
            count++;
            System.out.println("已经送了"+count+"个冰激凌");
        }
    }

    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
        for (int i = 0; i < 100; i++) {
           new Thread(atom).start();
        }


    }
}

synchronized具有原子性,但是速度比较慢:都要去判断锁 获得锁 释放锁 的过程


public class MyAtomThread extends Thread {



    private int count =0;
    private    Object lock=new Object();
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1. 从共享数据中读取数据到本线程栈中
            //2. 修改本线程栈中变量副本的值
            //3. 把本线程栈中变量副本的值赋值给共享数据
            synchronized (lock) {
                count++;
                System.out.println("已经送了"+count+"个冰激凌");
            }
        }
    }

    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
        for (int i = 0; i < 100; i++) {
           new Thread(atom).start();
        }
    }
}

使用原子类AtomicInteger类实现


import java.util.concurrent.atomic.AtomicInteger;

public class MyAtomThread extends Thread {
    private AtomicInteger ac = new AtomicInteger(0);
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1. 从共享数据中读取数据到本线程栈中
            //2. 修改本线程栈中变量副本的值
            //3. 把本线程栈中变量副本的值赋值给共享数据
            int count=ac.incrementAndGet();
            System.out.println("已经送了" + count + "个冰激凌");
        }
    }
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }


}

2. 原子类 AtomicInteger

2.1 AtomicInteger原理:自旋锁+CAS算法

在这里插入图片描述
在这里插入图片描述


import java.util.concurrent.atomic.AtomicInteger;

public class MyAtomDemo1 {
    public static void main(String[] args) {
        //初始化一个默认值为0的原子型Integer
        AtomicInteger ac=new AtomicInteger();
        System.out.println(ac);//0
        //初始化一个指定值的原子型Integer
        AtomicInteger ac1=new AtomicInteger(10);
        System.out.println(ac1);//10

        //1. get 获取值
        AtomicInteger ac2=new AtomicInteger(10);
        System.out.println(ac2.get());//10

        //2. getAndIncrement() 以原子方式将当前值+1,注:这里返回的是自增前的值
        AtomicInteger ac3=new AtomicInteger(10);
        System.out.println(ac3.getAndIncrement());// 还是10
        System.out.println(ac3.get());//实际值 +1=   11
        //3. incrementAndGet() 以原子方式将当前值+1,注:这里返回的是自增后的值
        AtomicInteger ac4=new AtomicInteger(10);
        System.out.println(ac4.incrementAndGet());// 变为11
        System.out.println(ac4.get());//实际值 11

        //4. addAndGet() 以原子方式将参数与对象中的值相加,并返回结果
        AtomicInteger ac5=new AtomicInteger(10);
        System.out.println(ac5.addAndGet(5));// 15
        System.out.println(ac5.get());//实际值 15


        //5. addAndGet() 以原子方式设置为newValue的值,并返回旧值
        AtomicInteger ac6=new AtomicInteger(10);
        System.out.println(ac6.getAndSet(20));// 10
        System.out.println(ac6.get());//实际值 20

    }
}

3 . synchronized和CAS的区别

在这里插入图片描述

4 并发工具类 Hashtable

在这里插入图片描述

HashMap线程不安全


import java.util.HashMap;

public class MyHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        HashMap<String,String> map=new HashMap<>();

        Thread t1=new Thread(()->{
            for (int i = 0; i < 25; i++) {
                map.put(i+"",i+"");
            }

        });
        Thread t2=new Thread(()->{
            for (int i = 25; i < 51; i++) {
                map.put(i+"",i+"");
            }

        });
        t1.start();
        t2.start();

        System.out.println("-----------");
        //让main线程睡眠 为了t1t2线程数据添加
        //1 2 3 4 5 6
        Thread.sleep(1000);
        for (int i = 0; i < 51; i++) {
            System.out.println(map.get(i + ""));
        }

    }
}

在这里插入图片描述
HashTable线程安全


import java.util.Hashtable;

public class MyHashTableDemo {
    public static void main(String[] args) throws InterruptedException {
        Hashtable<String,String> map=new Hashtable<>();

        Thread t1=new Thread(()->{
            for (int i = 0; i < 25; i++) {
                map.put(i+"",i+"");
            }

        });
        Thread t2=new Thread(()->{
            for (int i = 25; i < 51; i++) {
                map.put(i+"",i+"");
            }

        });
        t1.start();
        t2.start();

        System.out.println("-----------");
        //让main线程睡眠 为了t1t2线程数据添加
        //1 2 3 4 5 6
        Thread.sleep(1000);
        for (int i = 0; i < 51; i++) {
            System.out.println(map.get(i + ""));
        }

    }
}

4.1 Hashtable效率低的原因

在这里插入图片描述

5. ConcurrentHashMap 线程安全&效率高

在这里插入图片描述


import java.util.concurrent.ConcurrentHashMap;

public class MyConcurrentHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<String,String> map=new ConcurrentHashMap<>();

        Thread t1=new Thread(()->{
            for (int i = 0; i < 25; i++) {
                map.put(i+"",i+"");
            }

        });
        Thread t2=new Thread(()->{
            for (int i = 25; i < 51; i++) {
                map.put(i+"",i+"");
            }

        });
        t1.start();
        t2.start();

        System.out.println("-----------");
        //让main线程睡眠 为了t1t2线程数据添加
        //1 2 3 4 5 6
        Thread.sleep(1000);
        for (int i = 0; i < 51; i++) {
            System.out.println(map.get(i + ""));
        }

    }
}

在这里插入图片描述

5.1 ConcurrentHashMap-jdk1.7版本

最多同时16个线程同时执行
在这里插入图片描述

5.2 ConcurrentHashMap-jdk1.8版本

1.7需要二次哈希,大数组不能扩充
在这里插入图片描述
ctrl+shift+alt+u看源码结构
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6. CountDownLatch的使用

使用场景:让一条线程等待其他线程执行完毕后再执行
控制线程运行额类
主要方法
在这里插入图片描述
在这里插入图片描述


import java.util.concurrent.CountDownLatch;

class  ChildThread1 extends Thread {
    public CountDownLatch countDownLatch;
    public ChildThread1(CountDownLatch countDownLatch) {
        this.countDownLatch=countDownLatch;
    }
    @Override
    public void run() {
        //1.吃饺子
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"正在吃第"+i+"个饺子");
        }
        //2. 吃完汇报
        countDownLatch.countDown();

    }
}
class  ChildThread2 extends Thread {
    public CountDownLatch countDownLatch;
    public ChildThread2(CountDownLatch countDownLatch) {
        this.countDownLatch=countDownLatch;
    }
    @Override
    public void run() {
        //1.吃饺子
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"正在吃第"+i+"个饺子");
        }
        //2. 吃完汇报
        countDownLatch.countDown();
    }
}
class  ChildThread3 extends Thread {
    public CountDownLatch countDownLatch;
    public ChildThread3(CountDownLatch countDownLatch) {
        this.countDownLatch=countDownLatch;
    }
    @Override
    public void run() {
        //1.吃饺子
        for (int i = 0; i < 15; i++) {
            System.out.println(Thread.currentThread().getName()+"正在吃第"+i+"个饺子");
        }
        //2. 吃完汇报
        //当每次调用countDown时 计数器就会-1
        countDownLatch.countDown();
    }
}
class  MotherThread extends Thread {
    public CountDownLatch countDownLatch;
    public MotherThread(CountDownLatch countDownLatch) {
        this.countDownLatch=countDownLatch;
    }
    @Override
    public void run() {
       //1.等待
        try {
            //当计数器变为0时,会自动唤醒等待的线程
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //2.收拾碗筷
        System.out.println(Thread.currentThread().getName()+"准备收拾碗筷");

    }
}
public class MyCountDownLatchDemo {


    public static void main(String[] args) {
        //1. 创建CountDownLatch的对象,此处需要传递四个线程
        //在底层实现了一个计数器,此时计数器的值是3
        CountDownLatch countDownLatch=new CountDownLatch(3);
        //创建4个线程并全部开启
        MotherThread m=new MotherThread(countDownLatch);
        m.setName("妈妈");
        m.start();
        ChildThread1 c1=new ChildThread1(countDownLatch);
        ChildThread2 c2=new ChildThread2(countDownLatch);
        ChildThread3 c3=new ChildThread3(countDownLatch);
        c1.setName("小红");
        c2.setName("小蓝");
        c3.setName("小黑");
        c3.start();
        c1.start();
        c2.start();


    }

}

在这里插入图片描述

7. Semaphore的使用

使用场景:可以控制访问特定资源的线程数量
在这里插入图片描述


import java.util.concurrent.Semaphore;

class MyRunnable implements Runnable {

    private Semaphore semaphore;

    public MyRunnable(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    //1.获得管理员对象
    @Override
    public void run() {
        //2.获得通行证
        try {
            semaphore.acquire();
            //3.开始通过
            System.out.println("已获得通行证,正在通行ing");
            Thread.sleep(2000);
            System.out.println("归还通行证");
            //4.归还通行证
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class MySemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2); //最多通行几条线程
        MyRunnable myRunnable = new MyRunnable(semaphore);
        for (int i = 0; i < 10; i++) {
            new Thread(myRunnable).start();
        }
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱喝阔落的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值