多线程MS

多线程

多线程

1. 串行 / 并行 / 并发 有什么区别?并发编程 的目的是什么?

  • 串行单条线程按照先后顺序执行多个任务
  • 并发在一台CPU处理器上 "同时 " 处理多个任务
  • 并行在多台CPU处理器上同时处理多个任务
  • 并发编程的目的充分利用处理器的每一个核,以达到最高的处理性能

2. 线程和进程的区别?

  • 进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。
  • 线程是进程的一个实体,是cpu调度和分派的基本单位,是一个能独立运行的基本单位,同一进程中多个线程之间可以并发执行,以提高效率

3. Java中线程有哪几类?

  • 用户线程 User Thread
  • 守护线程 Daemon Thread:作用就是为其他线程的运行提供便利服务,比如垃圾回收线程就是一个守护线程
  • 两者区别:虚拟机的离开。如果User Thread已经全部运行完毕了,只剩Daemon Thread了,这时候虚拟机会直接离开,因为Daemon Thread没有可守护的线程了,所以程序没有继续运行必要了。

4. 创建线程有哪几种方式?

①继承Thread类,重写run方法,run方法体就是线程执行体。
②实现Runnable接口,重写run方法,run方法体就是线程执行体。
③实现Callable接口,重写call方法,通过FutureTask类包装Callable实现类再创建线程,FutureTask类已经实现了Runnable接口,call方法体就是线程执行体。

class H implements Callable {


    @Override
    public Object call() throws Exception {
        return null;
    }


    public static void main(String[] args) {
        H h =new H();
        FutureTask task=new FutureTask(h);
        Thread thread=new Thread(task);
        thread.start();
    }
}

5.说以下Runnable和Callable的区别?

  • Runnable接口中的run方法没有返回值
  • Callable接口中call方法有返回值,是1个泛型,和FutureTask配合可以获取异步执行的结果

6.线程都有哪些状态?

  • ①新建状态(new): 新创建了一个线程对象
  • ②可运行状态(runnable):线程对象创建后,其他线程调用了该对象的start方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权
  • ③运行状态(running):可运行的线程获取到了cpu的使用权,执行程序代码
  • ④阻塞状态(block):是指线程因为某种原因放弃了cpu的使用权被暂停,sleep和wait方法都可以导致线程阻塞。
  • ⑤死亡状态(dead):线程run方法、main方法执行结束,或因异常退出run方法,则该线程的生命周期结束,死亡线程不可再次复生。
    在这里插入图片描述
  • java中的线程状态已枚举的方式列出
public enum State {
   
    /**
     *  尚未启动的线程的线程状态
     */
     
    NEW,

    /**
     * 可运行线程的线程状态。可运行线程
     * 状态正在Java虚拟机中执行,但它可能
     * 等待来自操作系统的其他资源
     * 如处理器
     */
     
    RUNNABLE,

    /**
     *  线程的线程状态被阻塞,等待监视器锁定
     *  处于阻塞状态的线程正在等待监视器锁定
     *  输入同步块/方法或
     *  调用后重新输入同步块/方法
     *   {@link Object#wait()Object.wait}
     */
    
    BLOCKED,

    /**
     * 等待线程的线程状态。
     * 由于调用其中一个线程,线程处于等待状态
     * 以下方法:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>处于等待状态的线程正在等待另一个线程执行特定操作。
     *
     *例如,一个调用了<tt> Object.wait()</ tt>的线程
     *在对象上正在等待另一个线程调用
     * <tt> Object.notify()</ tt>或<tt> Object.notifyAll()</ tt>
     *那个对象。名为<tt> Thread.join()</ tt>的线程
     *正在等待指定线程终止。
     */
    WAITING,

    /**
    * 具有指定等待时间的等待线程的线程状态。
     *线程由于调用了以下之一而处于定时等待状态
     *以下方法具有指定的正等待时间:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     *终止线程的线程状态。
     *线程已完成执行。
     */
    TERMINATED;
}
  • 线程状态监测测试
class Test{
    public static void main(String[] args) throws InterruptedException {
         Thread thread = new Thread(()->{
             for (int i = 0; i < 5; i++) {
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
             System.out.println(Thread.currentThread().getName()+": end....");
         });
        //NEW
        System.out.println("state01 = " + thread.getState().toString());

        thread.start();
        //RUNNABLE
        System.out.println("state02 = " + thread.getState().toString());

        //线程状态监测:线程状态不处于dead就输出
        while (thread.getState()!=Thread.State.TERMINATED){
            Thread.sleep(200);
            System.out.println("stateNow = " + thread.getState().toString());
        }
    }
}
	state01 = NEW
	state02 = RUNNABLE
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	stateNow = TIMED_WAITING
	Thread-0: end....
	stateNow = TERMINATED

在这里插入图片描述
在这里插入图片描述

7.sleep()和wait()方法的区别?

  • ① 方法来源:sleep是Thread的方法,wait是Object的方法。
  • ② 释放锁:sleep方法没有释放锁。wait方法释放了锁,使得其他线程可以抢到锁访问同步代码块或同步方法。
  • ③ 使用范围:wait,notify和notifyAll只能在同步方法或块中使用。sleep方法可以在任何地方使用。
  • ④ 捕获异常:sleep必须捕获异常。wait方法不需要捕获异常。

8.notify和notifyAll方法有什么区别?

  • 如果线程调用了对象的wait的方法,那么线程便会进入该对象的等待池中(等待池中线程不会去竞争该对象的锁)。
  • 当有线程调用了notify方法(随机唤醒一个wait状态线程)后,只有一个随机线程会从等待池中进入锁池中。如果调用的是notifyAll方法(唤醒所有wait状态线程),则会将等待池中所有线程移动到锁池中,等待锁竞争。

9.run方法和start方法的区别?

  • 一个线程对象对应一个run方法,run方法是线程执行体,负责完成线程具体内容,run方法运行结束,此线程就终止。
  • start方法用来启动一个线程,真正实现了多线程的运行。多个线程对象先后调用start方法,此时无需等待run方法体代码执行完毕,也可以直接运行下面的代码。

10.创建线程的池有哪几种方式?

  • ①newFixedThreadPool(int nThreads)
    创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。
  • ②newCachedThreadPool()
    创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
  • ③newSingleThreadExecutor()
    这是一个单线程的Executor,它创建单个
  • ④newScheduledThreadPool(int corePoolSize)
    创建一个固定长度的线程池,而且以延迟或定时的方式执行任务。

11.线程池有哪几种状态?

  • 线程池有五种状态:Running,ShutDown,Stop,Tidying,Terminated。
    在这里插入图片描述

12.线程池的submit()和execute()方法有什么区别?

  • ①接收参数: 接收的参数不一样
  • ②返回值:submit返回future,而execute无返回值
  • ③异常处理:submit方法方便异常处理
    submit方法执行线程得到一个future,如果调用这个future的API去获取结果,例如future.get(),如果线程中有异常产生,就可以通过在submit方法周围环绕try…catch来捕获这个异常。

13.JAVA程序中怎么保证多线程的安全运行?

多线程的安全性体现在以下3个方面:

  • ①原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(Atomic开头的原子类,synchronized,Lock可以解决原子性问题)
  • ②可见性:一个线程对共享变量修改可以及时地被其他线程看到(synchronized,volatile,Lock可以解决可见性问题)
  • ③ 有序性:程序执行的顺序按照代码的先后顺序执行(happens-before原则可以实现有序性)

14.多线程锁的升级原理是什么?

java中,锁共有四种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁,这几个状态会随着竞争情况而逐渐升级。锁可以升级但不能降级。
锁升级的示意图:
在这里插入图片描述

15.什么是死锁?本地怎么检测死锁?线上又怎么检测?

  • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,如无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程成为死锁进程。
        Lock A = new ReentrantLock();
        Lock B = new ReentrantLock();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":尝试获取锁A....");
                A.lock();
                System.out.println(Thread.currentThread().getName()+":获取A锁成功!!!");
                //保证让锁B先让线程2获取到
                Thread.sleep(1000);

                System.out.println(Thread.currentThread().getName()+":尝试获取锁B....");
                B.lock();
                System.out.println(Thread.currentThread().getName()+":获取B锁成功!!!");
            }
        },"线程1").start();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":尝试获取锁B....");
                B.lock();
                System.out.println(Thread.currentThread().getName()+":获取B锁成功!!!");
                //保证让锁A先让线程1获取到
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":尝试获取锁A....");
                A.lock();
                System.out.println(Thread.currentThread().getName()+":获取A锁成功!!!");

            }
        },"线程2").start();

    }
          控制台输出结果:
					线程1:尝试获取锁A....
					线程1:获取A锁成功!!!
					线程2:尝试获取锁B....
					线程2:获取B锁成功!!!
					线程1:尝试获取锁B....//线程1阻塞了,获取锁B失败
					线程2:尝试获取锁A....//线程2阻塞了,获取锁A失败

  • 使用JDK自带的工具Jconse去检测死锁
    • 1 打开jdk的bin目录下的jconsle.exe
      在这里插入图片描述
    • 2 点击线程下面的死锁
      在这里插入图片描述
    • 3 查看线程1的锁情况,可以看到锁的拥有者为线程2
      在这里插入图片描述
    • 4 查看线程2的锁情况,可以看到锁的拥有者为线程1
      在这里插入图片描述
  • Jconsole远程监控tomcat进程

16.怎么防止死锁?

  • ① 保证每个线程的加锁顺序都是一致的。
        Lock A = new ReentrantLock();
        Lock B = new ReentrantLock();
	        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                A.lock();
                ....
                B.lock();
                ....
            }
        },"线程1").start();
       
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                A.lock();
                ....
                B.lock();
                ....
            }
        },"线程1").start();
  • ② 获取锁的时候给一个超时时间,到达超时时间就放弃锁的竞争
        Lock A = new ReentrantLock();
        Lock B = new ReentrantLock();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":尝试获取锁A....");
                A.lock();
                System.out.println(Thread.currentThread().getName()+":获取A锁成功!!!");
                //保证让锁B先让线程2获取到
                Thread.sleep(1000);

                System.out.println(Thread.currentThread().getName()+":尝试获取锁B....");
               
                //设置做多尝试获取锁B 5秒
                B.tryLock(5,TimeUnit.SECONDS);
                System.out.println("锁B的使用情况 " + B);
            }
        },"线程1").start();
        new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":尝试获取锁B....");
                B.lock();
                System.out.println(Thread.currentThread().getName()+":获取B锁成功!!!");

                //保证让锁A先让线程1获取到
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":尝试获取锁A....");
               
                //设置做多尝试获取锁A 5秒
                A.tryLock(5, TimeUnit.SECONDS);
                System.out.println("锁A的使用情况 " + A.toString());
            }
        },"线程2").start();
  • ② 死锁检测
  • ThreadMXBean.findDeadlockedThreads()获取出现死锁的线程
        ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
        Lock A = new ReentrantLock();
        Lock B = new ReentrantLock();
        Thread thread1 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ":尝试获取锁A....");
                A.lock();
                System.out.println(Thread.currentThread().getName() + ":获取A锁成功!!!");
                //保证让锁B先让线程2获取到
                Thread.sleep(1000);

                System.out.println(Thread.currentThread().getName() + ":尝试获取锁B....");
                B.lock();
                System.out.println(Thread.currentThread().getName() + ":获取B锁成功!!!");
            }
        }, "线程1");
        thread1.start();

        Thread thread2 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ":尝试获取锁B....");
                B.lock();
                System.out.println(Thread.currentThread().getName() + ":获取B锁成功!!!");

                //保证让锁A先让线程1获取到
                Thread.sleep(1000);

                System.out.println(Thread.currentThread().getName() + ":尝试获取锁A....");
                A.lock();
                System.out.println(Thread.currentThread().getName() + ":获取A锁成功!!!");
            }
        }, "线程2");
        thread2.start();

            //先让死锁形成
            Thread.sleep(2000);
            long[] deadlockedThreads = mxBean.findDeadlockedThreads();
            if (deadlockedThreads==null){
                System.out.println("-------线程之间没有出现死锁-------");
            }else {
                System.out.println("--------线程之间出现死锁-----------");
                for (long deadId:deadlockedThreads) {
                    ThreadInfo threadInfo = mxBean.getThreadInfo(deadId,1000);
                    System.out.println("threadInfo = " + threadInfo);
                }
             thread1.stop();
             thread2.stop();
            }
    }
线程1:尝试获取锁A....
线程1:获取A锁成功!!!
线程2:尝试获取锁B....
线程2:获取B锁成功!!!
线程2:尝试获取锁A....
线程1:尝试获取锁B....
--------线程之间出现死锁-----------
threadInfo = "线程2" Id=13 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@15aeb7ab owned by "线程1" Id=12
	at sun.misc.Unsafe.park(Native Method)
	-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@15aeb7ab
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
	at com.wpq.demo01.TestCAS$2.run(TestCAS.java:86)
	...


threadInfo = "线程1" Id=12 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@7b23ec81 owned by "线程2" Id=13
	at sun.misc.Unsafe.park(Native Method)
	-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@7b23ec81
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
	at com.wpq.demo01.TestCAS$1.run(TestCAS.java:65)
	...

17.ThreadLocal是什么?有哪些使用场景?

  • 线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。
  • Java提供ThreadLocal类来支持线程局部变量(其实就是一个Map),ThreadLocal会为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,将对象的可见范围限制在同一个线程内,而不会影响其他线程所对应的副本。(这样做其实就是以空间交换时间的方式,以耗费内存为代价,但大大减少了线程同步所带来的性能消耗以及减少线程并发控制的复杂度,和synchronized相反)
//ThreadLocal类中提供的几个常用方法
 public T get() { }---获取ThreadLocal在当前线程中保存的变量副本
    public void set(T value) { }---设置当前线程中变量的副本
    public void remove() { }---移除当前线程中变量的副本
    protected T initialValue() { }---protected修饰的方法。
    ThreadLocal提供的只是一个浅拷贝,如果变量是一个引用类型,那么就要重写该函数来实现深拷贝。建议在使用ThreadLocal一开始时就重写该函数

  • 但是在管理环境下(如web服务器)使用线程局部变量要注意,因为在这种情况下,工作线程的生命周期比任何应用变量的生命周期要长。任何线程局部变量一旦在工作完成后没有释放java应用就存在内存泄露的风险。

18.synchronized和volatile的区别是什么?

  • volatile本质是告诉JVM当前变量在工作内存的值是不确定的,需从主内存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量,方法,和类机别。
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
  • volatile不会造成线程的阻塞;synchronized则可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量则可以被编译器优化;

19.synchronized和Lock的区别?

  • ①内置关键字:synchronized是Java内置关键字,在jvm层面,Lock是个Java类。
  • ②判断获取锁:synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
  • ③释放锁方式:synchronized会自动释放锁(a线程执行完同步代码会释放锁;b线程执行过程中发生异常会释放锁),Lock需要在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  • ④线程等待:使用synchronized关键字,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一致等待下去;而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
  • ⑤synchronized的锁可重入,不可中断,非公平,而Lock锁可重入,可判断,可公平(两者皆可)。
  • ⑥适用同步代码量:Lock锁适合大量代码的同步问题,synchronized适合少量代码的同步问题。

20.synchronized和ReentrantLock的区别是什么?

  • 本质区别:synchronized是java内置关键字(if,else,for,while),ReentrantLock是类(类就有各种方法,会更加灵活)。
  • ReentrantLock比synchronized的扩展性体现在以下几点上:
    • ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁。
    • ReentrantLock可以获取各种锁的信息。
    • ReentrantLock可以灵活地实现多路通知。

21.说一下atomic的原理?

Atomic包中的类基本的特性就是在多线程环境下,当多个线程同时对单个变量进行操作时,具有排他性。即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一致等待到执行成功。

  • Atomic系列的类中都会调用unsafe类中几个本地方法。unsafe类中包含了大量对C代码的操作,包括很多直接内存分配以及原子操作的调用,这里的大量方法调用都会存在安全隐患,需小心使用。
  • 如何保证原子性:自旋+CAS(乐观锁)
  • 适用场景:低并发有同步数据的情况下

22.什么是线程安全?

  • 在多线程环境下共享同一数据,线程安全的代码会通过同步机制(锁/同步代码块/同步方法)来保证每个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

23.线程安全的类有哪些?

  • StringBuffer
  • Vector(比arrayList多了线程安全)
  • statck(堆栈类,先进后出)
  • hashtable(比hashmap多了线程安全)
  • enumeration(枚举,相当于迭代器)

24.什么是原子操作?有哪些原子操作类?

  • 概念:所谓原子操作就是指不会被线程调度机制打断的一个操作或一系列的操作。
  • java原子操作类:
    • 原子更新基本类型:
      • ①AtomicBoolean:原子更新布尔类型。
      • ②AtomicInteger:原子更新整型。
      • ③AtomicLong:原子更新长整型。
    • 原子更新数组:
      • ①AtomicIntegerArray: 原子更新整型数组里的元素。
      • ②AtomicLongArray: 原子更新长整型数组里的元素。
      • ③AtomicReferenceArray: 原子更新引用类型数组里的元素。

25.volatile关键字的作用?

  • 保证了变量的可见性。被volatile关键字修饰的变量,如果值发生了改变,其他线程能够立马看见,避免了脏读的现象。

26.什么是乐观锁?什么是悲观锁?

  • 乐观锁: 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
  • 悲观锁: 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

27.什么是CAS?

  • CAS: 全称为Compare and Swap,即比较-替换。假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改为B并返回true,否则什么都不做并返回false。当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值A对某条线程来说,永远是一个不会变的值A,只要某次CAS操作失败,永远都不可能成功。
  • CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性,要想保证整个代码的原子性必须使用synchronized关键子。

28.什么是AQS?

  • AQS: 全称为AbstractQueuedSychronizer:抽象队列同步器。

  • 1.AQS应用 :

    • 如果说 JUC(java.util.concurrent) 的基础是CAS的话,那么AQS就是整个Java并发包的核心了,ReentrantLockReentrantReadWriteLock 等都是基于AQS实现的。
  • 2.AQS核心思想:

    • 如果共享资源空闲,则将当前请求资源的线程设为有效工作线程,并锁定共享资源。
    • 如果共享资源被占用,则需要一套线程阻塞排队和唤醒机制来分配锁。
  • 3.AQS原理:

    • AQS基于CLH同步双向队列,用volatile修饰共享变量state,请求线程通过CAS去改变状态,修改成功则获取锁成功,获取失败则将请求线程阻塞并封装成节点加入等待队列,等待被唤醒。

29.怎么检测一个线程是否拥有锁?

  • java.lang.Thread中有一个方法叫holdsLock(锁对象),它返回true如果当且仅当当前线程拥有某个具体对象的锁。
 synchronized (this){
                boolean b = Thread.holdsLock(this);
                System.out.println("b = " + b);
            }

30.生产者消费者模型的作用是什么?

  • ① 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率。
  • ② 解耦:意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不受到对方的约束。
  • 利用同步代码块的wait()方法和notify()方法解决生产者消费者问题
/**
 * 1.锁对象(面条)--协调生产者和消费者
 */
@Data
public class Noodles {
    //面条类型
    private String NoodleType="打卤面";
    //是否有面
    private boolean isNoodle = false;
}
/**
 * 2.消费者(顾客)
 */
public class Consumer implements Runnable {

    private Noodles noodles;
    //传入Noodles的一个实例,保证生产者和消费者共享一个锁对象noodles
    public Consumer(Noodles noodles){
        this.noodles = noodles;
    }

    @SneakyThrows
    @Override
    public void run() {
        while (true){
            synchronized (noodles){
                if (!noodles.isNoodle()){
                    //没面条-->消费者就等
                    noodles.wait();
                }
                //有面条-->消费者就吃
                System.out.println("顾客正在吃"+noodles.getNoodleType());
                //吃了四秒
                Thread.sleep(4000);

                System.out.println("顾客吃完了面....");
                //吃完了,把标记设为false,表示没面了
                noodles.setNoodle(false);


                System.out.println("顾客通知老板下面....");
                noodles.notify();
            }
        }
    }
}

/**
 * 3.生产者(老板)
 */
public class Producer  implements Runnable{
    private Noodles noodles;

    //传入Noodles的一个实例,保证生产者和消费者共享一个锁对象noodles
    public  Producer (Noodles noodles){
        this.noodles = noodles;
    }
    @SneakyThrows
    @Override
    public void run() {
        while(true){
            synchronized (noodles){
                if (noodles.isNoodle()){
                    //有面条-->老板休息
                    noodles.wait();
                }
                //没面的话-->老板就做
                System.out.println("老板正在做"+noodles.getNoodleType());
                //4秒钟把面做完了
                Thread.sleep(4000);

                System.out.println("老板做完了面....");
                //做完了面,把标记设为true表示有面了
                noodles.setNoodle(true);

                System.out.println("老板通知顾客吃面....");
                //通知消费者吃面
                noodles.notify();
            }
        }
    }
}

/*
* 4.测试类
*/
public class Test {

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        Noodles noodles = new Noodles();
        service.submit(new Consumer(noodles));
        service.submit(new Producer(noodles));
    }
}           控制台输出:
					老板正在做打卤面
					老板做完了面....
					老板通知顾客吃面....
					
					顾客正在吃打卤面
					顾客吃完了面....
					顾客通知老板下面....
					
					老板正在做打卤面
					老板做完了面....
					老板通知顾客吃面....
					
					顾客正在吃打卤面
					顾客吃完了面....
					顾客通知老板下面....
					老板正在做打卤面
					老板做完了面....
					.............

31.Thread类中的yieId方法有什么作用?

  • yieId方法:可以理解为"谦让"。它会使当前线程让掉CPU的执行权,使处于运行状态的线程变为就绪状态,并重新和其它同级线程竞争CPU调度权。
class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 开始执行...");
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName()+": 结束执行...");
    }
}
class Test{
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.submit(new MyThread());
        service.submit(new MyThread());
        service.shutdownNow();
    }
}

32.有三个线程T1,T2,T3,怎么保证它们按顺序执行?

  • 使用Thread类的join方法保证。
  • join方法的作用: 实现同步,它可以使得线程之间的并发执行变为串行执行。当调用某个线程的join方法时,主线程会等待该线程死亡才继续执行。
  • join方法的实现原理: 线程的join方法是通过调用线程的wait方法,把主线程放在线程对象的对象池中,让它放弃CPU的执行权,来达到同步的目的的。例如main线程中调用了线程A的join方法,则相当于在main线程中调用了线程A的wait方法,当线程A执行完(或者到达等待时间),线程A会自动调用自身的notifyAll方法唤醒main线程,从而达到同步的目的。
class ThreadTest{
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        Thread t2 = new Thread(myThread);
        Thread t3 = new Thread(myThread);
        t1.start();
       // 挂起main线程(当前正在执行代码的线程),等待t1执行完之后,再唤醒main线程
        t1.join();
        
        t2.start();
        t2.join();
        t3.start();
            /* 输出结果  Thread-1:0
						Thread-1:1
						Thread-1:2
						Thread-1:3
						Thread-1:4
						Thread-2:0
						Thread-2:1
						Thread-2:2
						Thread-2:3
						Thread-2:4
						Thread-3:0
						Thread-3:1
						Thread-3:2
						Thread-3:3
						Thread-3:4  */
    }
}
class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5 ; i++) {
            System.out.println("vip = "+i);
        }
    }
}
//调用线程的join方法-->让子线程插队,主线程被挂起,知道子线程死亡后重新被唤醒
class Test{
    public static void main(String[] args) throws InterruptedException {
       Thread vip = new Thread(new MyThread());
       vip.start();
        for (int i = 0; i < 10 ; i++) {
            if (i==5){
                vip.join();
            }
            System.out.println(Thread.currentThread().getName() +":"+ i );

        }
    }
}       
      控制台输出:
				main:0
				main:1
				main:2
				main:3
				main:4
				vip = 0
				vip = 1
				vip = 2
				vip = 3
				vip = 4
				main:5
				main:6
				main:7
				main:8
				main:9

33.什么是公平锁和非公平锁?synchronized和ReentrantLock分别是公平锁还是非公平锁?

  • 公平锁: 线程获取不到锁的时候,会自动加入队列,等待持有锁的线程释放锁后,队列中的第一个线程会获取锁,依次按照顺序获取锁。
  • 非公平锁: 线程获取不到锁的时候,会自动加入队列,等待持有锁的线程释放锁后,所有等待的线程会同时去竞争锁。
  • 可重入: 同一个线程可以反复获取同一个锁多次,然后需要释放多次。
  • synchronized 是非公平锁,可以重入。
  • ReentrantLock默认是非公平锁,可重入(可通过构造函数设置是否公平)。
     //公平锁
     ReentrantLock fairLock = new ReentrantLock(true);
     //非公平锁
     ReentrantLock noFairLock = new ReentrantLock(false);

34.thread.interrupt()方法

  • 如果线程处于正常状态,该方法只是把线程的中断标识置为true而并不是直接中断线程,因此需要在线程执行体中判断中断标识来决定是否继续执行线程任务。
  • 如果线程处于被阻塞状态(例如处于sleep状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常,并且继续运行
public static void main(String[] args) {
      Thread thread = new Thread(new Runnable() {
          @Override
          public void run() {
              //如果当前线程的中断标识 没有被设置为true才进入循环
              while (!Thread.interrupted()){
                  System.out.println("args ============ ");
                  
//                  try {
//                      Thread.sleep(100);
//                  } catch (InterruptedException e) {
//                      e.printStackTrace();
//                  }
                  
              }
          }
      });

      thread.start();

        for (int i = 0; i < 100000; i++) {
            System.out.println("i = " + i);
            if (i == 99999){
                thread.interrupt();
            }
        }
    }

35. Thread.interrupted()静态方法

  • 取出线程中断标识,并且重置线程中断标识
 public static boolean interrupted() {    
    return currentThread().isInterrupted(true); 
    // true表示重置线程中断标识为false
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值