1 并发编程的基础(实现/生命周期)

进程
 
进程的本质是一个正在执行的程序,程序运行时系统会创建一个进程,并且给每个进程分配独立的内存地址空间保证每个进程地址不会相互干扰。同时,在 CPU 对进程做时间片的切换时,保证进程切换过程中仍然要从进程切换之前运行的位置出开始执行。所以进程通常还会包括程序计数器、堆栈指针。
有了进程以后,可以让操作系统从宏观层面实现多应用并发。而并发的实现是通过 CPU 时间片不端切换执行的。对于单核 CPU 来说,在任意一个时刻只会有一个进程在被CPU 调度.

 

线程
 
在多核 CPU 中,利用多线程可以实现真正意义上的并行执行;在一个应用进程中,会存在多个同时执行的任务,如果其中一个任务被阻塞,将会引起不依赖该任务的任务也被阻塞。通过对不同任务创建不同的线程去处理,可以提升程序处理的实时性 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程更快。
 
 
 
一 线程的应用 
 
在 Java 中,有多种方式来实现多线程。继承 Thread 类、实现 Runnable 接口、使用 ExecutorService、Callable、Future 实现带返回结果的多线程
 
1.继承 Thread 类创建线程
 
Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread类的 start()实例方法。它会启动一个新线程,通过直接 extend Thread,并复写 run()方法,就可以启动新线程并执行自己定义的 run()方法。
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}

MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start(); 

 继承Thread类创建线程类

01. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。

public class MyThread extends Thread{

02. 创建Thread子类的实例,即创建了线程对象。

MyThread t1=new  MyThread();

03. 调用线程对象的start()方法来启动该线程。

t1.start();

 

 

2.实现 Runnable 接口创建线程 
 
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个 Runnable 接口 
public class MyThread extends OtherClass implements Runnable {
        public void run() {
            System.out.println("MyThread.run()");
        }
    }

实现Runnable接口创建线程类

01. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

public class MyThread1 implements Runnable{

02. 创建Runnable实现类的实例,并以此实例(mt)作为Thread的target来创建Thread对象(t1),该Thread对象才是真正的线程对象。

MyThread1 mt=new MyThread1();

// 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!

Thread t1=new Thread(mt);

03. 调用线程对象的start()方法来启动线程。

需要注意的是:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。

t1.start();

 

使用 ExecutorService、Callable、Future 实现带返回结果的多线程

实现 Callable 接口通过 FutureTask 包装器来创建 Thread 线程有的时候,我们可能需要让一步执行的线程在执行完成以后,提供一个返回值给到当前的主线程,主线程需要依赖这个值进行后续的逻辑处理,那么这个时候,就需要用到带返回值的线程了。Java 中提供了这样的实现方式 
public class CallableDemo implements Callable<String> {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            CallableDemo callableDemo = new CallableDemo();
            Future<String> future = executorService.submit(callableDemo);
            System.out.println(future.get());
            executorService.shutdown();
        }

        @Override
        public String call() throws Exception {
            int a = 1;
            int b = 2;
            System.out.println(a + b);
            return "执行结果:" + (a + b);
        }
    }

 

3.多线程的实际应用场景
基于业务开发来说,很多使用异步的场景我们都通过分布式消息队列来做了。应用得比较多的场景是在做文件跑批,每天会有一些比如收益文件、对账文件,我们会有一个定时任务去拿到数据然后通过线程去处理.
 
 
 
二 线程的生命周期

Java线程具有五种基本状态

1、新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new  MyThread();

2、就绪状态(Runnable):也被称为“可执行状态”。当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

3、运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

4、阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

(1)等待阻塞 -- 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

(2)同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

(3)其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 

 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Java 线程既然能够创建,那么也势必会被销毁,所以线程是存在生命周期的,那么我们接下来从线程的生命周期开始去了解线程。
线程一共有 6 种状态(NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED)
NEW:初始状态,线程被构建,但是还没有调用 start 方法
RUNNABLED:运行状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行中”
BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了 CPU 使用权,阻塞也分为几种情况
➢ 等待阻塞:运行的线程执行 wait 方法,jvm 会把当前线程放入到等待队列
➢ 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他线程锁占用了,那么 jvm 会把当前的线程放入到锁池中
➢ 其他阻塞:运行的线程执行 Thread.sleep 或者 t.join 方法,或者发出了 I/O 请求时,JVM 会把当前线程设置为阻塞状态,当 sleep 结束、join 线程终止、io 处理完毕则线程恢复
TIME_WAITING:超时等待状态,超时以后自动返回
TERMINATED:终止状态,表示当前线程执行完毕
 
 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

代码显示线程的状态

package com.gupaoedu.vip;

import java.util.concurrent.TimeUnit;
 
public class ThreadStatusDemo {
    public static void main(String[] args) {
        new Thread(()->{
            while(true){
                try {
                    TimeUnit.SECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Time_Waiting_Thread").start();
        new Thread(()->{
            while(true){
                synchronized (ThreadStatusDemo.class) {
                    try {
                        ThreadStatusDemo.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Wating_Thread").start();
    //BLOCKED
        new Thread(new BlockedDemo(),"Blocke01_Thread").start();
        new Thread(new BlockedDemo(),"Blocke02_Thread").start();
    }

    static class BlockedDemo extends  Thread{
        @Override
        public void run() {
            synchronized (BlockedDemo.class){
                while(true){
                    try {
                        TimeUnit.SECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}
E:\GUPAO\FILE\VIP专享--2019期录播视频及资料\03.分布式与高并发\01.分布式并发编程\01.初步认识多线程的发展及使用\课堂源码\20190509-并发编程第一次课源码\thread-demo\target\classes\com\gupaoe
du\vip>jps
5152 Launcher
5168 ThreadStatusDemo
5364
5476 Jps
5688 App
7160 KotlinCompileDaemon
6108 RemoteMavenServer36

E:\GUPAO\FILE\VIP专享--2019期录播视频及资料\03.分布式与高并发\01.分布式并发编程\01.初步认识多线程的发展及使用\课堂源码\20190509-并发编程第一次课源码\thread-demo\target\classes\com\gupaoe
du\vip>jstack 5168
2020-02-01 21:47:57
Full thread dump Java HotSpot(TM) Client VM (25.144-b01 mixed mode):

"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x14dd5c00 nid=0x1bb4 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"Blocke02_Thread" #14 prio=5 os_prio=0 tid=0x14dd5800 nid=0x8a8 waiting for monitor entry [0x14f7f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.gupaoedu.vip.ThreadStatusDemo$BlockedDemo.run(ThreadStatusDemo.java:46)
        - waiting to lock <0x04bbf470> (a java.lang.Class for com.gupaoedu.vip.ThreadStatusDemo$BlockedDemo)
        at java.lang.Thread.run(Thread.java:748)

"Blocke01_Thread" #12 prio=5 os_prio=0 tid=0x14dd5000 nid=0x16cc waiting on condition [0x1565f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.gupaoedu.vip.ThreadStatusDemo$BlockedDemo.run(ThreadStatusDemo.java:46)
        - locked <0x04bbf470> (a java.lang.Class for com.gupaoedu.vip.ThreadStatusDemo$BlockedDemo)
        at java.lang.Thread.run(Thread.java:748)

"Wating_Thread" #10 prio=5 os_prio=0 tid=0x14dd4800 nid=0x1f34 in Object.wait() [0x1589f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x04a00390> (a java.lang.Class for com.gupaoedu.vip.ThreadStatusDemo)
        at java.lang.Object.wait(Object.java:502)
        at com.gupaoedu.vip.ThreadStatusDemo.lambda$main$1(ThreadStatusDemo.java:27)
        - locked <0x04a00390> (a java.lang.Class for com.gupaoedu.vip.ThreadStatusDemo)
        at com.gupaoedu.vip.ThreadStatusDemo$$Lambda$2/23237446.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Time_Waiting_Thread" #9 prio=5 os_prio=0 tid=0x14dd3400 nid=0x15b4 waiting on condition [0x1575f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.gupaoedu.vip.ThreadStatusDemo.lambda$main$0(ThreadStatusDemo.java:16)
        at com.gupaoedu.vip.ThreadStatusDemo$$Lambda$1/24037599.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #8 daemon prio=9 os_prio=0 tid=0x14d06800 nid=0x18cc runnable [0x00000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x14d07800 nid=0x1c20 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x14ce8000 nid=0xa80 runnable [0x1510f000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x049d32d0> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x049d32d0> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x14cd1c00 nid=0xb10 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x14cb8800 nid=0x1044 runnable [0x00000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x14ca9c00 nid=0x18fc in Object.wait() [0x1509f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x04807ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x04807ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x14c83000 nid=0x151c in Object.wait() [0x024ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x04805f68> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x04805f68> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x14c7bc00 nid=0x1eb0 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x14d97800 nid=0x109c waiting on condition

JNI global references: 336
启动一个线程前,最好为这个线程设置线程名称,因为这样在使用 jstack 分析程序或者进行问题排查时,就会给开发人员提供一些提示显示线程的状态
➢ 运行该示例,打开终端或者命令提示符,键入“jps”,(JDK1.5 提供的一个显示当前所有 java 进程 pid 的命令)
➢ 根据上一步骤获得的 pid,继续输入 jstack pid(jstack是 java 虚拟机自带的一种堆栈跟踪工具。jstack 用于打印出给定的 java 进程 ID 或 core file 或远程调试服务的 Java 堆栈信息)
通过上面的分析,我们了解到了线程的生命周期,线程在整个生命周期中并不是固定的处于某个状态,而是随着代码的执行在不同的状态之间进行切换。
 
 
 
三 .线程的启动终止
 
线程的启动
前面我们通过一些案例演示了线程的启动,也就是调用start()方法去启动一个线程,当 run 方法中的代码执行完毕以后,线程的生命周期也将终止。启动线程的唯一方法就是通过 Thread类的 start()实例方法。它会启动一个新线程,通过直接 extend Thread,并复写 run()方法,就可以启动新线程并执行自己定义的 run()方法。
 
线程的终止
要优雅的去中断一个线程,在线程中提供了一个 interrupt方法
interrupt 方法当其他线程通过调用当前线程的 interrupt 方法,表示向当前线程打个招呼,告诉他可以中断线程的执行了,至于什么时候中断,取决于当前线程自己。
线程通过检查资深是否被中断来进行相应,可以通过isInterrupted()来判断是否被中断。
通过下面这个例子,来实现了线程终止的逻辑
package com.gupaoedu.vip;

import java.util.concurrent.TimeUnit;
 
public class InterruptDemo {

    private static int i;

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            //如果不是中斷狀態則i++
            while(!Thread.currentThread().isInterrupted()){//默认是false  _interrupted state?
                i++;
            }
            System.out.println("i:"+i);
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt(); //把isInterrupted设置成true
    }
}
这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。
 
thread.interrupt();将判断标志设置成true,默认判断标志为false,然后通过两种中断方式来中断线程。
//1. Thread.interrupted()  通过复位中断异常
//2. InterruptedException 通过抛出异常中断线程

 

 

Thread.interrupted
上面的案例中,通过 interrupt,设置了一个标识告诉线程可 以 终 止 了 , 线程中还提供了静态方法Thread.interrupted()对设置中断标识的线程复位。比如在上面的案例中,外面的线程调用 thread.interrupt 来设置中断标识,而在线程里面,又通过 Thread.interrupted 把线程的标识又进行了复位。
 

为什么要复位(设置成默认状态false)

Thread.interrupted()是属于当前线程的,是当前线程对外界中断信号的一个响应,表示自己已经得到了中断信号,但不会立刻中断自己,具体什么时候中断由自己决定,让外界知道在自身中断前,他的中断状态仍然是 false,这就是复位的原因。 

 

 

 

 

线程的终止原理
 thread.interrupt()方法实际就是设置一个 interrupted 状态标识为 true、并且通过ParkEvent 的 unpark 方法来唤醒线程。
1. 对于 synchronized 阻塞的线程,被唤醒以后会继续尝试获取锁,如果失败仍然可能被 park
2. 在调用 ParkEvent 的 park 方法之前,会先判断线程的中断状态,如果为 true,会清除当前线程的中断标识
3. Object.wait 、 Thread.sleep 、 Thread.join 会 抛 出InterruptedException
 
 
这里给大家普及一个知识点,为什么 Object.wait、Thread.sleep和Thread.join都 会 抛 出InterruptedException?
发现这几个方法有一个共同点,都是属于阻塞的方法而阻塞方法的释放会取决于一些外部的事件,但是阻塞方法可能因为等不到外部的触发事件而导致无法终止,所以它允许一个线程请求自己来停止它正在做的事情。当一个方法抛出 InterruptedException 时,它是在告诉调用者如果执行该方法的线程被中断,它会尝试停止正在做的事情并且通过抛出 InterruptedException 表示提前返回。所以,这个异常的意思是表示一个阻塞被其他线程中断了然 后 , 由 于 线 程 调 用 了 interrupt() 中 断 方 法 , 那 么Object.wait、Thread.sleep 等被阻塞的线程被唤醒以后会通过 is_interrupted 方法判断中断标识的状态变化,如果发现中断标识为 true,则先清除中断标识,然后抛出InterruptedException
 需要注意的是,InterruptedException 异常的抛出并不意味着线程必须终止,而是提醒当前线程有中断的操作发生,至于接下来怎么处理取决于线程本身,比如
1. 直接捕获异常不做任何处理      2. 将异常往外抛出    3. 停止当前线程,并打印异常信息
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 
 
 
 
 
 
 
 

多线程知识点问答

 

1创建线程的几种方式? Wait,sleep分别是谁的方法,区别?线程间的通信方式?

继承Thread类创建线程,实现Runnable接口创建线程,使用Callable和Future创建线程 ,使用线程池例如用Executor框架。   Wait是在Object.java中的方法,sleep是在Thread.java中的方法。 区别是wait是当前运行线程进入阻塞状态并释放锁,sleep使线程进入阻塞状态但不释放锁。   使用wait和notify实现线程间的通信。

 

2 介绍下什么是死锁,遇见过死锁么?你是怎么排查的?(可以通过JPS排查)

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,通过jps排查。

 

3 创建线程池的几种方式,线程池有什么好处?

单线程的线程池  固定大小的线程池 一个可缓存的线程池 一个大小无限的线程池

频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池可以使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务。

 

4 线程继承和接口的区别,接口有什么好处?

java中我们想要实现多线程常用的有两种方法,继承Thread 类和实现Runnable 接口,有经验的程序员都会

选择实现Runnable接口 ,其主要原因有以下两点:首先,java只能单继承,因此如果是采用继承Thread的方法,那么在以后进行代码重构的时候可能会遇到问题,因为你无法继承别的类了。其次,如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

 

5 synchronized ,lock ,reentrantlock 的区别,用法及原理

ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁;ReenTrantLock提供了一个Condition(条件)类,可实现分组唤醒线程,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程;ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。synchronized不可中断

6 countDownLatch 与 CyclicBarrier 的用法

CountDownLatch的计数器,线程完成一个记录一个,计数器是递减 计数器,只能使用一次;CyclicBarrier的计数器 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行,计数器是递增 计数器提供reset功能,可以多次使用

 

7 ThreadLocal 的用法及原理

ThreadLocal用处就是用来把实例变量共享成全局变量,在程序的任何方法中都可以访问到该实例变量;

线程隔离的原理, ThreadLocalMap类是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取,每个线程中都有一个独立的ThreadLocalMap副本(key-ThreadLocal,value-副本),它所存储的值(副本),只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了专属变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。

 

8 volatile 关键字的作用及用法

  保证内存可见性 volatile关键字用于声明简单类型变量,如intfloat boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。

 

9 乐观锁和悲观锁

 

 

10 对公平锁,非公平锁,可重入锁,自旋锁,读写锁的理解

锁是基于线程的分配,可重入锁: 可以被单个线程多次获取。可中断锁: 某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,种就是可中断锁。 公平锁:即尽量以请求锁的顺序来获取锁。自旋锁,一个线程A在获得普通锁后,如果再有线程B试图获取锁,那么这个线程B将会(阻塞)。读写所:读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁,正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。

 

11 CAS是什么及底层原理

CAS是一条CPU并发原语。判断内存某个位置的值是否为预期值,如果是更改为新值,这个过程是原子的。

底层原理:Unsafe类是CAS的核心类,由于java方法无法直接访问底层系统,需要通过本地(native)方法来访问,基于该类可以直接操作特定内存的数据。Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务

12 ArrayBlockingQueue,LinkedBlockQueue,SynchronousQueue等等堵塞队列的理解

SynchronousQueue是无界的,是一种无缓冲的等待队列,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加;可以认为SynchronousQueue是一个缓存值为1的阻塞队列

LinkedBlockingQueue是无界的,是一个无界缓存的等待队列。当队列缓冲区达到最大值缓存容量时,才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。

在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

ArrayBlockingQueue是有界的,是一个有界缓存的等待队列。在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;ArrayBlockingQueue和LinkedBlockingQueue是两个最普通、最常用的阻塞队列,一般情况下,处理多线程间的生产者消费者问题,使用这两个类足以。

 

13 ThreadPoolExecutor 的传入参数及内部工作原理

14 给你一个具体的业务场景,让你使用ThreadPoolExecutor 创建一个合适的线程池

15 分布式环境下怎么保证线程安全。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

posted @ 2020-02-01 20:28  LPJのBLOG  Views(200)  Comments(0)    收藏  举报