7.并发编程

 1.线程和进程的关系?

一个进程可以有多个线程,但必须有一个线程,一个线程只能在一个进程的范围内活动。

 

2.并发和并行的区别?

并发是指有公共资源的争夺,并行是没有公共资源的争夺。

 

3.实现多线程的方式?

    1>.继承Thred类,重写run接口。

    2>.实现Runnable接口,实现run方法。

    3>.通过Callable接口,实现call方法。

 

4.什么是守护线程?

专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了

 

5. synchoried原理?

这个原本是个重量级锁,但是在后来更新中进行了优化,引入了偏向锁和轻量级锁。通过锁膨胀一步步升级为重量级锁。

以前的文章:https://blog.csdn.net/huaixiaohai_1/article/details/92607179

还是说原理吧:JVM基于进入和退出Monitor对象来实现同步和代码块同步,同步代码块是通过monitorenter和monitorexit实现的。同步方法是通过另一种方式实现的(JVM规范中并没有说明)。但是还是可以使用这种方法,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit插入在结束或者异常的位置。必须对应。

 

6.volicate原理?

volicate主要是通过Lock前缀指令来实现的。

lock前缀指令会引起处理器缓存写回到内存中。当一个处理器的缓存写回到内存中会导致其他处理器的缓存无效。

还有一个作用是他会禁止指令重排序。

 

7.禁止指令重排序是什么原理?

通过内存屏障

 

8.原子类实现原理?

以AutomicInteger为例进行源码分析:

 // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

上面这段代码的意思是:JVM可以实现Java对象的布局,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之类。jdk内部使用的工具类sun.misc.Unsafe提供的方法objectFieldOffset()用于获取某个字段相对Java对象的“起始地址”的偏移量,可以使用这个偏移量调用getInt、getLong、getObject等方法来获取某个Java对象的某个字段。
还可以看到value通过volatile保证可见性:

//两个构造方法 
public AtomicInteger(int initialValue) {
        value = initialValue;
    }
 public AtomicInteger() {
    }
//get和set方法
public final int get() {
        return value;
    }

public final void set(int newValue) {
        value = newValue;
    }

看一下关键方法:更新操作

public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
 public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

他首先备份了一份原来的值,然后通过cas的方式一直while直到成功。

 

9. 信号量机制?

信号量机制是为了保护共享变量,使得一个线程只有一个时刻只有一个进程访问资源。

主要通过P(申请资源)V(释放资源)操作来实现信号量,对信号量的操作都是原子性的。

信号量S:用来记录资源数量,看是否能满足申请需要。

P(S) 若 S > 0:则S-1;线程继续执行,若S<0则挂起线程。

V(S) 若S>0;则S+1;若S<0,则唤醒一个线程。

 

10.AQS是什么,讲讲?

抽象的队列同步器,是除了java自带的synchronized关键字之外的锁机制。

如果请求的资源空闲,那么将当前请求资源设置为有效的工作线程,并将共享资源设置为锁定状态。如果请求的资源锁定,那么就进入CLH阻塞队列。

CLH阻塞队列:是一个虚拟双向队列,不存在实际的队列,仅记录节点之间的关联关系。

实际上AQS就是基于CLH队列,用volicate修饰共享变量state,线符程通过CAS去修改状态符。成功则获取锁成功,失败则进入等待队列。在这里插入图片描述

看这个图就清除多了。

 

11.为什么要用线程池?

当并发数量多的时候,频繁的创建和销毁线程费时间,所以采用线程池,让每个线程可以多次使用,减少消耗。

 

12.线程池的种类以及自定义线程池?

java提供了构建线程池,使线程池使用变得简单起来了。

 public ThreadPoolExecutor(
                              //初始线程数
                              int corePoolSize,
                              //最大线程个数
                              int maximumPoolSize,
                              //生存时间,超过初始线程个数以后的线程池使用后的销毁时间
                              long keepAliveTime,
                              //时间单位
                              TimeUnit unit,
                              //队列内部的使用数据结构
                              BlockingQueue<Runnable> workQueue)

    1>.单线程池(newSingleThreadExecutor)

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

这个线程池可以清楚的看到初始线和最大线程都只有1个线程。

  2>.定长线程池(newFixedThreadPool)

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

可以看到最大和初始化的线程数一样。适合任务量比较固定且时间消耗比较长的

 3>.可缓存线程池(newCachedThreadPool)

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

这个线程数量基本可以无限加,但是没用后60s之后就消失了。比较以用于任务量大但是消耗时间比较小的。

 4>.大小无限制的线程池(newScheduledThreadPool)

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

这个线程池也时固定大小的,构造方法里边指定,最大线程数也是无限加,非核心线程创建完以后就销毁。但是这个线程池使用的DelayedWorkQueue,意味着他会把任务排序,执行完以后循环。适用于定时任务和固定周期的任务。

自定义线程池根据业务需求使用ThreadPoolExecutor去创建,

这些可能也不全,而且理解也不够透彻,并发编程的水太深了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值