1、进程就是程序运行的内存空间,包含程序运行所需的资源
2、线程就是程序的最小执行单元,进程由多个线程组成,每个进程至少有一个主线程
3、守护线程 daemon thread, 守护线程依赖于创建它的线程,会伴随被守护线程的消亡而消亡,jvm中的垃圾收集器就是一个守护线程
4、线程new出来后,进入1就绪状态,start的之后,进入2可运行状态,得到cpu执行权就进入3运行状态,未得到cpu执行权进入4阻塞状态,运行时被wait进入5等待状态,运行完毕或者异常中断进入6死亡状态。
5、线程的三种创建方式
1)继承new Thread(),重写run方法,优点是编写简单,缺点是单继承方式,无法实现资源共享
2)实现Runnable接口,实现run方法,优点是可以多继承,可以实现资源共享,缺点是编写稍微复杂,
3)通过Callable、Future方式,实现callable接口,实现call方法(线程的执行方法,有返回值),Future包装该实现类对象,再用Thread包装Future对象,优点是Future对象实例,可以拿到线程执行方法的返回值
4)通过线程池创建线程。
6、Java多线程中调用wait() 和 sleep()方法有什么不同?
1)sleep方法,是线程的方法,wait/notify是object(以此对象作为锁)的方法
2)sleep,线程让出cpu资源,不释放锁,sleep指定时间结束后,线程重新争夺cpu执行权;wait,线程让出cpu资源,也释放锁,进入等待队列,当调用notify方法时,线程结束等待,重新争抢锁。
7、join()、yield()有什么区别
join:当前线程会等待调用join的线程执行完毕后再执行;yield:调用yield的线程会暂时放弃cpu执行权,同其他线程再次争夺cpu执行权
8、ThreadLocal的作用和原理?
ThreadLocal变量,在每个线程中拥有独自的实例,不被其他线程共享,主要用于在同一个线程方法间共享,在不同线程间不共享。
其底层是一个以当前线程实例为key的map,这个map的value也是一个键值对,它的key是一个ThreadLocal实列,值是具体的数据。
9、volatile关键字的原理?
volatile关键字与java内存模型有关,
java内存模型中,每个线程工作在自己的工作内存中,如果主内存的中的存在某个共享变量,工作内存中会存有这个共享变量的副本,线程运行时操作的是这个副本,完成操作后会把副本写入主内存,
这样做的底层原因是因为,cpu在工作时如果从内存中取数据会比较慢,所以会把数据的副本先读取存放到cpu缓存中(也就是寄存器)。
但是这样做的会产生一个问题,当多个线程操作这个共享变量时,会存在线程安全问题,当某个线程把一个变量修改后还没有写入主内存,另一个线程也修改了这个变量,造成变量的数据和预期不符。
要解决这个问题,可以用Synchronized关键字,但是会造成效率的下降,而volatile关键字可以利用它的特性解决这个问题。
volatile解决这个问题的逻辑可以理解为,被它修饰的变量再被某个线程修改后,会向所有其他线程发个通知, 告知这个变量已经被修改了,如果操作这个变量请重新去主内存读取最新的值。
这个特性称之为可见性。而其底层原理则是通过内存屏障来阻止指令重排。
并发编程要保证安全需要遵从以下规则:
可见性:一个线程修改共享变量,能够被其它线程同时感知。
原子性:一个操作的所有指令,要么执行完成,要么就不执行。
有序性:即程序执行的顺序按照代码的先后顺序执行。
先行发生原则:如果一个事件必须发生在另一个事件之前,结果必须反映,即使这些事件是乱序执行的。
指令重排:JVM在编译Java代码的时候,或者CPU在执行JVM字节码的时候,对现有的指令顺序进行重新排序,为的是不改变程序结果条件下优化效率,注意这里不改变程序结果指的是单线程的情况下。
内存屏障:内存屏障(Memory Barrier)是一种CPU指令,意味着在屏障之前发布的操作被保证在屏障之后发布的操作之前执行。