目录
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规范将程序
映射
成通用文件格式
指令重排,可能会发生在以下各个阶段: