线程池(详细)& Volatile关键字

声明:我是使用了黑马的资源,因为我感觉黑马基础这里讲的通俗易懂些,且对于画图还是满擅长,便于小白理解。

这里再说下线程的转换状态

线程池出现就是为了解决线程的弊端

 代码如下,在两个线程之间插入sleep会只有一条线程是因为第一个线程执行完毕后把线程归还给池子,那么此时线程池就有一条空闲的线程,第二个线程看池子的时候会看有没有空闲的线程,如果有就把第一个线程复用。这里没有sleep会有两条线程是因为第一条线程还来不及归还给池子第二个任务就提交过来了,所以线程池只能自己创建个新的线程

创建线程池的第一种方法

//static ExecutorService newCachedThreadPool​()   创建一个默认的线程池
//static newFixedThreadPool​(int nThreads)	    创建一个指定最多线程数量的线程池

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

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

        //1,创建一个默认的线程池对象.池子中默认是空的.默认最多可以容纳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();
    }
}

创建线程池第二种方法

代码实现,这里论证了参数代表的线程数不是初始值而是最大值

使用断点或者ThreadPoolExecutor强转

//static ExecutorService newFixedThreadPool​(int nThreads)
//创建一个指定最多线程数量的线程池

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

public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        //参数不是初始值而是最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());//0

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

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

        System.out.println(pool.getPoolSize());//2
//        executorService.shutdown();
    }
}

ThreadPoolExecutor类——自己创建线程池对象的方法

这里面的七个参数比较多,代码实现

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

public class MyThreadPoolDemo3 {
//    参数一:核心线程数量
//    参数二:最大线程数
//    参数三:空闲线程最大存活时间
//    参数四:时间单位
//    参数五:任务队列
//    参数六:创建线程工厂
//    参数七:任务的拒绝策略
    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();
    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "在执行了");
    }
}

创建线程对象参数详解

 

//    参数一:核心线程数量
//    参数二:最大线程数
//    参数三:空闲线程最大存活时间
//    参数四:时间单位  ---TimeUnit
//    参数五:任务队列  ---让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并且执行
//    参数六:创建线程工厂    ---按照默认的方式创建线程对象
//    参数七:任务的拒绝策略 ---- 1、什么时候拒绝任务  当提交的任务 > 线程池最大线程数量 + 队列容量
                        // 2、如何拒绝

volatile关键字——不具备原子性

问题表述

问题代码如下,这里B同学修改了值 但是A同学不知道 始终是在while那里

public class Money {
    public static volatile int money = 100000;
}
public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(Money.money == 100000){

        }

        System.out.println("结婚基金已经不是十万了");
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Money.money = 90000;
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("小路同学");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("小皮同学");
        t2.start();
    }
}

从JMM角度去理解以上情况

在JMM中堆内存是唯一的,而栈内存是线程独有的,也就是说每个线程都有一个自己的线程栈,这样获取变量值得速度会更快。

解决办法二:使用锁对象的方式:强制线程去看共享数据里的最新值

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("小路同学");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("小皮同学");
        t2.start();
    }
}
public class Money {
    //定义锁对象
    public static Object lock = new Object();
    public static volatile int money = 100000;
}
public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(true){
            synchronized (Money.lock){
                if(Money.money != 100000){
                    System.out.println("结婚基金已经不是十万了");
                    break;
                }
            }
        }
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        synchronized (Money.lock) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Money.money = 90000;
        }
    }
}

 

好的,我将回答你的问题。首先,Java中的ReentrantLock是一种可重入的互斥,它的底层实现依赖于CAS(Compare and Swap,比较并交换)算法和AQS(AbstractQueuedSynchronizer,抽象队列同步器)框架。CAS算法是一种无算法,在Java中通过sun.misc.Unsafe类来实现。它利用CPU底层的原子指令完成对内存中的数据进行原子性操作,保证了数据的一致性。而AQS框架则是一种实现、信号量等同步操作的基础框架,主要通过 FIFO 队列实现等待机制,同时提供了公平和非公平两种实现方式。 Java内存模型是一种抽象的概念,它定义了各种线程在何时、何地和如何访问共享内存。Java内存模型通过保证原子性、可见性和有序性来保证线程之间的并发执行正确性。其中原子性是指操作的不可分割性,比如对于32位的数据,它们必须被一次性地读取或写入,不能被分解成两个16位的操作。可见性是指对于共享变量的修改对于其他线程来说是可见的,主要通过volatile关键字synchronized关键字来保证。有序性是指执行顺序必须满足一定规则,比如一个事件在发生前必须先发生另一个事件。 Java线程池是一种可以提高程序性能的技术,在Java中通过ThreadPoolExecutor类来实现。它主要包括核心线程池、任务队列、最大线程池、线程工厂和拒绝策略等几个部分。其中核心线程池和最大线程池决定了线程池的线程数量,任务队列决定了线程池中的任务调度策略,线程工厂则决定了线程池中线程的创建方式,而拒绝策略则是当任务队列已经满了且线程池中的线程已被占用时,如何处理新的任务请求。 最后来回答你的问题:volatile关键字是一种Java线程间的同步机制,它保证一个变量在多个线程之间的可见性,也能保证一定程度的指令重排序。当一个变量被声明为volatile后,所有线程都能看到这个变量的最新值,而不管这个变量是否在本地CPU缓存中。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值