目录
1、进程和线程的由来
2、进程和线程的区别
3、Thread中start和run方法的区别
4、Thread和Runnable是什么关系
5、如何给run()方法传参
6、如何实现处理线程的返回值
7、线程的六个状态
8、sleep和wait区别
9、锁池EntryList
10、等待池WaitSet
11、notify和notifyAll的区别
12、yield函数
13、如何中断线程
14、线程安全问题的主要诱因
15、互斥锁的特性
16、synchronized的基础
17、Monitor锁的竞争、获取与释放
18、自旋锁与自适应自旋锁
19、synchronized的四种状态
20、偏向锁
21、轻量级锁
22、偏向锁、轻量级锁、重量级锁比较汇总
23、Synchronized 和 Reentrantlock区别总结
24、JMM与JAVA内存区域划分是不同概念层次
25、JMM如何解决可见性问题
26、happens-before的八大原则
27、volatile:JVM提供轻量级同步机制
28、volatile变量为何立即可见?
29、单例双重检测实现
30、volatile和synchronized的区别
31、CAS (Compare and Swap) 乐观锁
32、CAS是一个种高效是高效线程安全性的方法
33、CAS思想
34、CAS缺点
35、 Java线程池
36、 线程池中的Fork/Join框架
37、为什么要使用线程池
38、线程池中Executor的框架
39、ThreadPoolExecutor构造函数(最重要)
40、handler:线程池的饱和策略
41、新任务提交execute执行后的判断
42、线程池的状态
43、线程池大小如何选定
考察我们有没有用过线程
public class TreadTest {
private static void attack(){
System.out.println("Fight");
System.out.println("current Thread is:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t = new Thread(){
public void run(){//thread里实现run方法
attack();
}
};
System.out.println("current Thread is" + Thread.currentThread().getName());
}
}
输出为
current Thread is:main
Fight
current Thread is:main
public class TreadTest {
private static void attack(){
System.out.println("Fight");
System.out.println("current Thread is:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t = new Thread(){
public void run(){//thread里实现run方法
attack();
}
};
System.out.println("current Thread is:" + Thread.currentThread().getName());
t.start();
}
}
输出
current Thread is:main
Fight
current Thread is:Thread-0
public class Mythread extends Thread {
private String name;
public Mythread(String name){
this.name= name;
}
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("Thread Start:" + this.name + ",i = " + i);
}
}
public static void main(String[] args) {
Mythread mt1 = new Mythread("Thread1");
Mythread mt2 = new Mythread("Thread2");
Mythread mt3 = new Mythread("Thread3");
mt1.start();
mt2.start();
mt3.start();
}
}
输出
Thread Start:Thread1,i = 0
Thread Start:Thread1,i = 1
Thread Start:Thread1,i = 2
此处的thread2跑到thread1前面去了
Thread Start:Thread2,i = 0
Thread Start:Thread1,i = 3
Thread Start:Thread1,i = 4
Thread Start:Thread1,i = 5
Thread Start:Thread1,i = 6
Thread Start:Thread1,i = 7
Thread Start:Thread1,i = 8
。。。。。
Thread Start:Thread3,i = 1
Thread Start:Thread3,i = 2
Thread Start:Thread3,i = 3
Thread Start:Thread3,i = 4
Thread Start:Thread3,i = 5
Runnable
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name){
this.name= name;
}
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("Thread Start:" + this.name + ",i = " + i);
}
}
public static void main(String[] args) {
MyRunnable mr1 = new MyRunnable("Runnable1");
MyRunnable mr2 = new MyRunnable("Runnable2");
MyRunnable mr3 = new MyRunnable("Runnable3");
//没有start方法,需要创建thread
Thread t1 = new Thread(mr1);
Thread t2 = new Thread(mr2);
Thread t3 = new Thread(mr3);
t1.start();
t2.start();
t3.start();
}
}
输出显示
Thread Start:Runnable1,i = 0
Thread Start:Runnable1,i = 1
Thread Start:Runnable1,i = 2
Thread Start:Runnable1,i = 3
Thread Start:Runnable1,i = 4
Thread Start:Runnable1,i = 5
Thread Start:Runnable1,i = 6
Thread Start:Runnable1,i = 7
同样存在thread2在thread1前面
Thread Start:Runnable2,i = 0
Thread Start:Runnable1,i = 8
Thread Start:Runnable2,i = 1
Thread Start:Runnable1,i = 9
通过前面程序看出
因为有的程序执行依赖于子任务返回值执行的,当子任务交给子线程完成时,需获取他们的返回值,整个时候该怎么做。
实现方式有三种
没有设置Timeout参数的Object.wait()方法
没有设置Timeout参数的Thread.join()方法、LockSupport.park()方法
thread.sleep()方法
设置Timeout参数的Object.wait()方法
设置Timeout参数的Thread.join()方法、
LockSupport.parkNanos()方法
LockSupport.parkUntil()方法
最本质区别
假设线程A已经拥有了某个对象(不是类)的锁,而其他线程B、C想要调用这个对象的某个synchronized方法或者(块),由于B、C线程在进入对象synchronized方法或者块之前必须先获得对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会阻塞,进入一个地方等待锁的释放,这个地方便是该对象的锁池。
假设线程A调用某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争对象的锁。
当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出cpu的使用的暗示,但是线程调度器可能会忽略这个暗示
(1)调用interrupt(),通知线程应该中断。通知线程应该中断,具体时中断还是执行,应该由线程自己处理
(2)需要被调用的线程配合中断
即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一时间只有一个线程对需要同步的代码块进行访问。互斥性也称为操作的原子性
必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的,否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致。
Moitor 存在于每个java对象的对象头中,synchronized锁就是靠这种方式获取锁,这是为什么java任意对象可以作为锁的原因
许多情况下,共享数据的锁定状态持续时间较短,切换线程不值得在多处理器条件下,让另一个没有获取锁的线程在门外等待一会,但不放弃cpu的执行时间。这个不放弃cpu的执行的等待时间就是自旋。
缺点:若锁被其他线程长时间占用,会带来许多性能上的开销,在线程自旋时始终占用cpu的时间片,占用时间太长,字片时间会白消耗,如果超过,可以用PreBlockSpin来更改。
无锁、偏向锁、轻量级锁、重量级锁
锁膨胀方向:无锁——》偏向锁——》轻量级锁——》重量级锁
无锁:并没有加入任何锁,此时目标塑胶并没有任何线程占用。
减少同一线程获取锁的代价
大多数情况下,锁不存在多线程竞争,总是由同一线程多次获取。
如果一个线程获得了锁,那么锁就进入偏向模式,此时MarkWord的结构也变为偏向锁结构,当该线程再次请求锁时,无需再做任何同步操作,及获取锁的过程只需要检查MarkWord的锁标记记位为偏向锁以及当前线程id等于MarkWord的ThreadId即可,这样省去大量有关锁申请的操作
不适用锁竞争比较激烈的多线程场景
轻量级锁是由偏向锁升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁进程时候,偏向素就会升级为轻量级锁。
适应场景:线程交替执行同步块
若存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
在JMM中主内存属于共享数据区域,以某个程度来讲,包括堆和方法区。而工作内存数据线程私有区域,从某程度讲,包括程序接受器,虚拟机栈和本地机栈。
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
//第一此检测
if (instance == null) {
//同步
synchronized (Singleton.class) {
if (instance == null) {
//多线程环境下可能出现问题的地方
instance = new Singleton();
}
}
}
return instance;
}
}
上述代码存在多线程环境下仍有隐患,
原因在于某个线程在执行到第一次检测时候,读到Instance不为空时,Instanct的对象可能还没有完全初始化。
(1)volatile本质是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主内存中读取;
synchronzied 则是所动当前变量,只有当前线程可以访问该变量,其他线程被阻塞住直到该线程完成变量操作为止。
(2)volatile仅能使用变量级别
synchronized则可以使用变量,方法和类级别
(3)volatile仅能实现变量的修改可见性,不能保证原子性
synchronized则可以保存变量修改的可见性和原子性
(4)volatile不会造成线程阻塞
synchronized可能会造成线程的阻塞
(5)volatile标记的变量不会被编译器优化
synchronized标记的变量可以被编译器优化
web开发中,服务器需要接受并处理请求,所以为一个请求来分配一个线程来进行处理,如果并发的线程数量非常打,但执行的时间很短,这样就会频繁创建和销毁线程。日次就会大大降低系统效率,可能出现服务器在为每个请求创建新线程和销毁线程花费时间,消耗系统资源要比是西安处理的用户请求的时间和资源更多。