线程和进程

  • 进程:进程是程序的一次执行过程,是系统运行程序的基本单位。进程是动态的,随着程序的运行而创建,结束就消亡。

  • 线程:与进程相似,比进程更小,一个进程在执行时可以产生多个线程。

同类的多个线程可以共享进程的堆和方法区,但虚拟机,本地方法栈,程序计数器为线程私有。


并发与并行

  • 并发:同一时间段,多个任务都在执行(单位时间内不一定同时执行)

  • 并行:单位时间内多个任务同时执行。


Java中的线程有几种状态

  1. 新建状态:新创建了一个线程对象。

  2. 就绪状态:线程对象创建之后,其它线程调用了该对象的start()方法,此时线程处于就绪状态,并未运行,等待获取CPU的使用权。

  3. 运行状态:就绪状态的线程获取到CPU的使用权之后,执行程序代码。

  4. 阻塞状态:阻塞状态是线程因为某些原因放弃了CPU的使用权,暂停运行,直到线程到就绪态,才有机会再到运行状态。

  5. 死亡状态:线程执行完了,或者线程因为异常退出了run()方法,该线程结束生命周期。


死锁

线程A持有资源2,线程B持有资源1,他们都想申请对方的资源,然后这两个线程就会相互等待造成死锁。 死锁


产生死锁的条件

  1. 互斥条件:该资源任意时刻只能由一个线程占用。

  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已经获得资源保持不放。

  3. 不可剥夺条件:线程已获得的资源在未使用完之前,不能被其它线程强行剥夺,只能自己释放。

  4. 循环等待条件:若干个进程之间形成一种头尾相接的循环等待资源关系。


如何避免死锁

破坏四个条件中的一个就好。

  1. 破坏互斥条件:这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的。

  2. 破坏请求与保持条件:一次性申请所有资源。

  3. 破坏不可剥夺条件:占用部分资源的线程申请其它资源,申请不到就主动释放自己占有的资源。

  4. 破坏循环等待条件:按序申请资源,按反序释放资源。


sleep()方法和wait()方法的区别和共同点

  • 共同点:

    • 两者都可以暂停线程的执行。
  • 区别:

    • Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。
    • wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。

为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

new 一个 Thread,线程进入了新建状态;调用 start() 方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。