面试准备 --- 线程生命周期

面试准备,因为是学习过程中的笔记,所以无法保证所有理解都是正确的,慎,欢迎纠正

正文

线程

说到线程,自然要说进程,具体的我不想说多,一般都学过操作系统,定义理论什么的考试都考过了,说一下书上看到的最好的关于线程进程的理解.
简单来说,进程像是一个容器,比如一个房子,房子里面有卧室,厨房,书房,洗手间等,当然,还有一家三口,当妈妈带女儿外出时,爸爸一个人在家,这时爸爸就像一个线程,想干什么干什么,房子里只有他一个(进程程中只有一个线程,实际不可能,还有守护线程).当三个人住在一起时,相当于三个线程,就会有一些小冲突,比如,女儿看动画片时,爸爸就不能看体育频道了,这就是线程间的资源竞争,当然大部分情况下,线程之间还是协作关系,毕竟一家人如果只是竞争,还在一起干什么.

线程周期

这里用一张图kK2XH1.png

  • 新建

    • 一般就使用Thread thread = new Thread() ;
    • 当然,上面创建的线程内部什么都没干,你可以使用继承Thread类,重写run方法,然后使用上面的方法创建实例即可.但是继承是十分宝贵的资源,所以使用实现Runnable接口,这时使用public Thread(Runnable target);,在Java8中,我们有更简单的写法,使用Lambda表达式new Thread(lambda),在lambda的位置写逻辑的lambda表达式形式的内容即可.
    • 这里有一个很重要的概念,我在线程池重用的博客中提到过,就是run和start的区别,虽然很简单,但如果没有注意到,对于线程池的理解会不易.只有start才能启动一个线程,运行线程实例的run方法只是使用原来的线程运行了该方法.
  • 终止线程

    • 按理来说,线程执行完毕就会关闭,但凡是都有例外,一些服务线程可能常驻系统,他们不会结束.
    • JDK提供一个方法stop()用于主动终止线程,但是这是一个被标注废弃的方法,为什么,因为这个线程太过于暴力,stop()方法在结束线程时,会立即释放这个线程持有的所有锁,在并发情况下,这会是一个怎样的悲剧,结果不言而喻.
    • 如何手动结束一个线程呢,很简单,只要我们决定何时退出run方法即可.将所有逻辑写在一个死循环中,设置一个标志变量,按自己的逻辑修改变量,然后在合适的地方判断,然后退出循环即可.
  • 线程中断(阻塞)

    • 需要理清终止,中断,等待的区别,终止是线程终止,而中断是线程并未结束,只是给线程发送一个通知,告诉线程有人希望你退出,至于结果怎样,完全由收到通知的线程自行决定,即如果不设置中断逻辑,即使通知中断,也不会有什么用.
    • 三个方法:
      • public void Thread.interrupt() 中断线程
      • public boolean Thread.isInterrupt() 判断是否被中断
      • public static boolean Thread.interrupted() 判断是否被中断,并清除当前中断状态
      • 第一个通知线程中断,设置中断标志位,第二个通过检查中断标志位来判断是否被中断,第三个同第二个,不过会在检查后清除标志位.
    • 如何设置中断逻辑,还是很简单,在run方法中,首先判断当前线程是否被中断,如果被中断,在条件语句内部写下中断逻辑即可.
  • 等待(wait)和通知(notify)

    • public final void wait() throws InterruptedException
    • public final native void notify()
    • 注意,这两个方法不是Thread的方法,而是Object的实例方法.当执行wait方法后,当前线程就会在这个对象上等待,也就是说停止执行,直到其他线程调用该对象的notify方法,还是注意,这是object的方法,所以一定是一个对象调用的,唤醒时也是该对象实例.
    • 实际的工作并不是那么简单,逻辑上是这样的的.多线程下,每一个对象持有一个等待队列,因为可能有多个线程执行了该对象的wait方法,当有线程执行notify时,会随机选择一个等待中的线程唤醒,对,是随机,并不是先进先出.还有一个方法notifyAll(),顾名思义了,就是唤醒所有等待队列中的线程.
    • 一个重要的点,wait和notify方法不能随便用,需要在synchronized代码块中使用,否则会有java.lang.IllegalMonitorStateException异常.原因是这样的,调用wait方法的线程必须获得调用方法的对象的对象监视器锁(只需要知道有这样一个东西即可),而获得一个对象的监视器锁有两种方法,一是同步代码块,而是同步方法.
  • 挂起(suspend)和继续执行(resume)

    • 这两个方法是Thread类的方法,那不是比wait和notify好用的多吗?其实这两个方法也是被废弃的方法.主要原因是因为suspend操作执行后,该线程会暂停(不是等待,线程状态还是运行中),但不会释放占有的锁,如果resume方法意外的在suspend方法前执行,那么这个线程很难再继续执行,同时它占有的锁就永远不会被释放,很容易造成系统死锁.
  • 等待线程结束(join)和让步(yeild)

    • join方法表示无限等待(或者通过参数设置最大等待时限),它会一直阻塞当前线程,知道目标线程执行完毕.怎么理解当前线程和目标线程呢.比如,你在主线程内执行t.join,主线程就是当前线程,t就是目标线程,它会让t的线程任务执行完毕后,再执行主线程这一句后面的内容.
    • 实际上,join方法内部就是当前线程实例调用wait方法,仔细想想
    • 然后是yield方法,public static native void yield()这是一个静态的本地方法,一旦执行,就会让当前线程让出CPU,注意,让出不是等待,相当于重新让所有线层回到同一起跑线,再次竞争.

其他概念

  • 守护线程(daemon)

    • 守护线程是系统后台运行的线程,当应用系统中只有守护线程时,虚拟机就会退出,就好像小说中的守护者,守护的对象不存在了,自己也就不需要存在了.
    • gc就是守护线程
  • synchronized

    • 这是一个关键字,用于实现线程间的同步,其实就是对同步的代码加锁,使同时只有一个线程进入同步块;
    • 三种用法:
      • 对象同步,就是同步代码块锁住一个对象,进入代码块要获得对应对象的锁.
      • 实例方法同步,同步方法,相当于对当前实例加锁
      • 静态方法同步,对类加锁.
      • 这三种加锁,锁粒度在大部分情况下是递增的.想不明白,多想想.

猜你喜欢

转载自blog.csdn.net/qq_36865108/article/details/86674403