1. 线程与进程的区别?
- 地址空间: 同一进程的线程共享本进程的地址空间,进程是独立的地址空间。
- 资源拥有: 同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
- 健壮性: 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
- 性能: 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
- 执行过程: 每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 基本区别: 线程是处理器调度的基本单位,但是进程不是。
2. Thread 与 Runnable
- 适合多个相同的程序代码的线程去处理同一个资源
- 可以避免java中的单继承的限制
- 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
3. 线程返回值
- 主线程等待
- Thread的join方法
- 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;
}
}
- 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());
}
}
- 线程池获取
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的状态
- New
- Runnable
- Waiting
- Timed Waiting
- Blocked
- Terminated
5. sleep与wait?
- sleep不释放锁,wait释放所对象
- sleep是Thread的方法,wait是Object方法
6. 锁池与等待池?
- 锁池: 假设线程A已经拥有某个对象的锁,由于B,C要调用这个对象的synchronized方法,B,C对象就会进入锁池。
- 等待池: A线程调用wait()后,A释放该对象的锁,A就会进入该对象的等待池。
7. notify与notifyall?
- notifyAll会让所有等待池的线程全部进入锁池去竞争锁机会
- notify是随机选取一个
8. yield是暗示cpu释放
9. interrupt函数
- 被抛弃的函数:stop
- interrupt是通知线程需要被中断,如果线程处于被阻塞状态,那么线程将抛出InterruptedException异常
- 如果线程处于活动状态,则将标志位设为true。被中断线程将继续执行,不受影响。
10. 线程状态之间的转化
synchronized
对象锁
- 同步代码块中的 this
- 同步非静态方法
类锁
- 同步代码块 类.class
- 同步静态方法
重入
同一对象进入同一资源
自旋锁和自适应自旋锁
- 自旋锁:通过让线程执行忙循环等待锁释放,不让出cpu(锁被占用时长较长则更耗费性能,所以会有自旋次数限制)
- 自适应自旋锁:自选次数不好设定,自适应自旋锁会根据上次自旋时间决定是否自旋
锁消除:
对上下文进行检测,去除不可能存在竞争的锁
锁粗化
通过加锁的范围,减少反复加锁与减锁
sychronized四种状态
锁膨胀的方向;无锁-->偏向锁-->轻量级锁-->重量级锁
- 偏向锁:如果一个线程获得了锁,那么锁进入偏向模式,可省去锁申请的操作
- 轻量级锁:线程交替执行
- 重量级锁:
锁的内存语义
- 锁释放:java内存模型会把该线程对应的本地变量刷新到主内存
- 锁获取:会将该线程对应的本地内存置为无效,临界区必须从主内存读取共享变量
sycronized 与 ReentryLock的区别
- 公平锁:获取锁的顺序按照先后调用lock方法顺序
- 非公平锁:抢占顺序不一定
happens-before
- 程序次序
- 锁定
- volatile
- 传递
- 线程启动
- 线程中断
- 线程终结
- 对象终结
悲观锁 与 乐观锁
- 悲观锁始终假设冲突
- 乐观锁只在提交数据时检测(CAS,J.U.C)