多线程考点

2,402 阅读4分钟

1. 线程与进程的区别?

  1. 地址空间: 同一进程的线程共享本进程的地址空间,进程是独立的地址空间。
  2. 资源拥有: 同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
  3. 健壮性: 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  4. 性能: 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
  5. 执行过程: 每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  6. 基本区别: 线程是处理器调度的基本单位,但是进程不是。

2. Thread 与 Runnable

  1. 适合多个相同的程序代码的线程去处理同一个资源
  2. 可以避免java中的单继承的限制
  3. 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

3. 线程返回值

  1. 主线程等待
  2. Thread的join方法
  3. Callable接口:FutureTask Or 线程池获取

Callable接口实现:


import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception{
        String value="test";
        System.out.println("Ready to work");
        Thread.currentThread().sleep(5000);
        System.out.println("task done");
        return  value;
    }

}
  1. FutureTask:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<String>(new MyCallable());
        new Thread(task).start();
        if(!task.isDone()){
            System.out.println("task has not finished, please wait!");
        }
        System.out.println("task return: " + task.get());

    }
}
  1. 线程池获取
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        Future<String> future = newCachedThreadPool.submit(new MyCallable());
        if(!future.isDone()){
            System.out.println("task has not finished, please wait!");
        }
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally {
            newCachedThreadPool.shutdown();
        }
    }
}

4. Thread的状态

  1. New
  2. Runnable
  3. Waiting
  4. Timed Waiting
  5. Blocked
  6. Terminated

5. sleep与wait?

  1. sleep不释放锁,wait释放所对象
  2. sleep是Thread的方法,wait是Object方法

6. 锁池与等待池?

  1. 锁池: 假设线程A已经拥有某个对象的锁,由于B,C要调用这个对象的synchronized方法,B,C对象就会进入锁池。
  2. 等待池: A线程调用wait()后,A释放该对象的锁,A就会进入该对象的等待池。

7. notify与notifyall?

  1. notifyAll会让所有等待池的线程全部进入锁池去竞争锁机会
  2. notify是随机选取一个

8. yield是暗示cpu释放

9. interrupt函数

  1. 被抛弃的函数:stop
  2. interrupt是通知线程需要被中断,如果线程处于被阻塞状态,那么线程将抛出InterruptedException异常
  3. 如果线程处于活动状态,则将标志位设为true。被中断线程将继续执行,不受影响。

10. 线程状态之间的转化

synchronized

对象锁

  1. 同步代码块中的 this
  2. 同步非静态方法

类锁

  1. 同步代码块 类.class
  2. 同步静态方法

重入

同一对象进入同一资源

自旋锁和自适应自旋锁

  1. 自旋锁:通过让线程执行忙循环等待锁释放,不让出cpu(锁被占用时长较长则更耗费性能,所以会有自旋次数限制)
  2. 自适应自旋锁:自选次数不好设定,自适应自旋锁会根据上次自旋时间决定是否自旋

锁消除:

对上下文进行检测,去除不可能存在竞争的锁

锁粗化

通过加锁的范围,减少反复加锁与减锁

sychronized四种状态

锁膨胀的方向;无锁-->偏向锁-->轻量级锁-->重量级锁

  1. 偏向锁:如果一个线程获得了锁,那么锁进入偏向模式,可省去锁申请的操作
  2. 轻量级锁:线程交替执行
  3. 重量级锁:

锁的内存语义

  1. 锁释放:java内存模型会把该线程对应的本地变量刷新到主内存
  2. 锁获取:会将该线程对应的本地内存置为无效,临界区必须从主内存读取共享变量

sycronized 与 ReentryLock的区别

  1. 公平锁:获取锁的顺序按照先后调用lock方法顺序
  2. 非公平锁:抢占顺序不一定

happens-before

  1. 程序次序
  2. 锁定
  3. volatile
  4. 传递
  5. 线程启动
  6. 线程中断
  7. 线程终结
  8. 对象终结

悲观锁 与 乐观锁

  1. 悲观锁始终假设冲突
  2. 乐观锁只在提交数据时检测(CAS,J.U.C)