Java面试 -- 并发

线程和进程的区别

进程是程序的一次执行过程,是程序在执行过程中的分配和管理资源的基本单位,每个进程都有自己的进程空间;线程是CPU调度和分派的基本单位,线程是进程的一部分,一个进程可以有多个线程,但线程只能存在于一个进程中
进程和线程的形象比喻

线程的生命周期

线程至少有5种状态:初始态,执行态,等待态,就绪态,终止态
Java线程切换及其状态
在这里插入图片描述

死锁

多个线程同时被阻塞,它们中的一个或者很多个都在等待某个资源被释放,由于线程被无限期地阻塞,因此线程不可能正常终止

产生死锁的四个必要条件

互斥条件
请求和保持条件
不剥夺条件
循环等待条件

创建多线程的四种方法

继承 thread类
实现runnable接口
实现callable接口
线程池

Callable接口和FutureTask

创建一个实现callable的实现类,重写call方法
创建一个callable实现类的对象
将callable实现类的对象作为参数,传递给futuretask的构造器,创建FutureTask的对象
将FutureTask的对象作为参数再传递给thread类的构造器,创建thread对象,并启动
通过futuretask类的对象调用方法get获取线程中的返回值

package com.chen.ThreadTest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class NumThread implements Callable{
    private int sum=0;
    @Override
    public Object call() throws Exception {
        for (int i = 0; i <100; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                sum+=i;
            }
        }
        return sum;
    }
}
public class ThreadNew {
    public static void main(String[] args) {
        NumThread numThread = new NumThread();
        FutureTask futureTask = new FutureTask(numThread);
        Thread thread = new Thread(futureTask);
        thread.setName("线程一");
        thread.start();
        try {
            Object o = futureTask.get();
            System.out.println(Thread.currentThread().getName() + ":" + o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

线程池

为什么要使用线程池

降低资源消耗 通过重复利用已经建立的线程来降低线程创建和销毁造成的消耗
提高响应速度 当任务到达时,任务可以不需要等待线程创建就能立即执行
提高线程的可管理性 线程时稀缺资源 如果无限制的创建,不仅会消系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

四种线程池

newCachedThreadPool 缓存线程池
public static ExecutorService newCachedThreadPool()

先检查这个线程池有没有闲置的线程,如果有就把这个新任务加到这个线程中来,如果没有,就新建一个线程加入线程池并执行新任务
闲置的默认时间是60s,超过60s,这个线程就会被销毁

newFixedThreadPool 核心线程池
public static ExecutorService newFixedThreadPool(int corePoolSize);

和缓存线程池的原理是一样的,只是参数不一样,线程池中的线程数是指定的

newSchedualThreadPool 调度线程池
public static ScheduledExecutorService new ScheduledThreadPool(int corePoolSize)

该线程池内的线程可以推迟执行或者周期执行

newSingleThreadExecuor 单例线程池
public static ExecutorService newSingleThreadExecutor();

单例线程池,只含有一个线程

推荐的创建线程池的方法

    public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
                              int maximumPoolSize,//线程池的最大线程数
                              long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
                              ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
                              RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
                               ) 

当线程数量小于核心线程数时,新建线程
当线程数量大于等于核心线程数,任务队列没有满的时候,加入任务队列
当任务队列满时,若小于最大线程数,新建线程;当大于最大线程数时,按照饱和策略进行处理
在这里插入图片描述
饱和策略
AbortPolicy 直接抛出异常
CallerRunsPolicy 用调用者所在的线程来执行任务; (当前调用者线程执行任务完成后,再执行此任务)
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy:直接丢弃任务

饱和策略

示例代码

package com.chen.ThreadTest;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
    private static  final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 100;
    private static final Long KEEP_ALIVE_TIME = 1L;
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        for (int i = 0; i < 10; i++) {
            Runnable worker=new MyRunnable(""+i);
            executor.execute(worker);
        }
        executor.shutdown();
    }
}

sleep,wait,yield,join

sleep和wait的区别

sleep是线程thread的方法,而wait是object的方法
sleep不会释放锁,但是会让出cpu资源,wait会释放锁
thread.sleep在任何情况下都能执行,object.wait必须在synchronized代码块中执行

yield和join

A.yield() 释放cpu资源,使A从运行中转为可运行状态,然后A和B同时去竞争这个资源,所以当A.yield之后,下一个执行的也有可能是A
A.join() 则会插队B线程,直到A线程执行完毕,才轮到B(阻塞状态)

synchronized关键字

synchronize关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能由一个线程执行

谈谈synchronized和ReentrantLock的区别

synchronized 自动释放锁,而lock需要手动释放锁,synchronized依赖于jvm而lock依赖于api

volatile关键字

volatile关键字的主要作用就是保证变量的可见性(一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值),然后还要一个作用就是防止指令进行重排序优化

谈谈synchronized和volatile关键字的区别

volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块
volatile关键字能保证数据的可见性,但不能保证数据的原子性
volatile关键字主要用于解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性

ThreadLocal

实现每个线程都有自己的专属本地变量

代码下载地址: https://pan.quark.cn/s/bc087ffa872a "测控电路课后习题详解"文件.pdf是一份极具价值的学术资料,其中系统地阐述了测控电路的基础理论、系统构造、核心特性及其实际应用领域。 以下是对该文献的深入解读和系统梳理:1.1测控电路在测控系统中的核心功能测控电路在测控系统的整体架构中扮演着不可或缺的角色。 它承担着对传感器输出信号进行放大、滤除杂音、提取有效信息等关键任务,并且依据测量与控制的需求,执行必要的计算、处理与变换操作,最终输出能够驱动执行机构运作的指令信号。 测控电路作为测控系统中最具可塑性的部分,具备易于放大信号、转换模式、传输数据以及适应多样化应用场景的优势。 1.2决定测控电路精确度的关键要素影响测控电路精确度的核心要素包括:(1)噪声与干扰的存在;(2)失调现象与漂移效应,尤其是温度引起的漂移;(3)线性表现与保真度水平;(4)输入输出阻抗的特性影响。 在这些要素中,噪声干扰与失调漂移(含温度效应)是最为关键的因素,需要给予高度关注。 1.3测控电路的适应性表现测控电路在测控系统中展现出高度的适应性,具体表现在:* 具备选择特定信号、灵活实施各类转换以及进行信号处理与运算的能力* 实现模数转换与数模转换功能* 在直流与交流、电压与电流信号之间进行灵活转换* 在幅值、相位、频率与脉宽信号等不同参数间进行转换* 实现量程调整功能* 对信号实施多样化的处理与运算,如计算平均值、差值、峰值、绝对值,进行求导数、积分运算等,以及实现非线性环节的线性化处理、逻辑判断等操作1.4测量电路输入信号类型对电路结构设计的影响测量电路的输入信号类型对其电路结构设计产生显著影响。 依据传感器的类型差异,输入信号的形态也呈现多样性。 主要可分为...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值