线程知识小结

一.进程与线程
       进程:一个应用程序(exe文件)。
单进程:一个时刻只能够执行一个应用程序。
多进程:操作系统能够同时执行多个应用程序。因为CPU在某一个时刻只能够被一个程序所使用,本质上是因为CPU在以一个飞快速度在各个进程(应用程序)之间进行切换。
       线程:一个应用程序(进程)的执行分支(应用程序的分支功能)。
       线程与进程的区别:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源。

二.线程的生命周期及五种基本状态

关于Java中线程的生命周期,首先看一下下面这张较为经典的图:

上图中基本上囊括了Java中多线程各重要知识点。掌握了上图中的各知识点,Java中的多线程也就基本上掌握了。主要包括:

新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

1. 继承Thread类,并重载run()方法;每个线程都能拥有独立的一份(数据不共享),但会受到java里面单继承(一个子类只能够去继承一个父类)的约束,不利于功能更深扩展。

2. 实现runnable接口,并调用Thread提供的构造函数。各个线程之间实现数据的共享,不仅可以实现多个接口(功能扩展丰富),还可以继承别的类。

就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞 -- 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

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

三、状态间的相互转变

1.创建并运行线程

      当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。

2.挂起和唤醒线程

      一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后,可以通过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。

      虽然suspend和resume可以很方便地使线程挂起和唤醒,但由于使用这两个方法可能会造成一些不可预料的事情发生,因此,这两个方法被标识为deprecated(抗议)标记,这表明在以后的jdk版本中这两个方法可能被删除,所以尽量不要使用这两个方法来操作线程。下面的代码演示了sleep、suspend和resume三个方法的使用。

3.终止线程的三种方法

     ①使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

     ②使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

     ③使用interrupt方法中断线程。

四、线程的几个方法:

     join():等待此线程死亡后再继续,可使异步线程变为同步线程。

     interrupt():中断线程,被中断线程会抛InterruptedException。


     线程通信:wait(),notify():

     wait() 表示等待获取锁,执行了该方法的线程释放对象的锁,JVM会把该线程放到对象的等待池中,该线程等待其它线程唤醒。

     notify() 执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中,使线程由阻塞队列进入就绪状态。


      sleep():让当前正在执行的线程休眠,有一个用法可以代替yield函数——sleep(0)。

      yield():暂停当前正在执行的线程对象,并执行其他线程。也就是交出CPU一段时间。


      sleep和yield区别:

1、sleep()方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。 

2、当线程执行了sleep(long millis)方法后,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法后,将转到就绪状态。 

3、sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。

4、sleep()方法比yield()方法具有更好的移植性。


      如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下的办法之一:

1、 调整各个线程的优先级;

2、 让处于运行状态的线程调用Thread.sleep()方法;

3、 让处于运行状态的线程调用Thread.yield()方法;

4、 让处于运行状态的线程调用另一个线程的join()方法。

五、数据同步:

线程同步的特征: 

1、 如果一个同步代码块和非同步代码块同时操作共享资源,仍然会造成对共享资源的竞争。因为当一个线程执行一个对象的同步代码块时,其他的线程仍然可以执行对象的非同步代码块。(所谓的线程之间保持同步,是指不同的线程在执行同一个对象的同步代码块时,因为要获得对象的同步锁而互相牵制);

2、 每个对象都有唯一的同步锁;

3、 在静态方法前面可以使用synchronized修饰符;

4、 当一个线程开始执行同步代码块时,并不意味着必须以不间断的方式运行,进入同步代码块的线程可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并不释放对象锁,只是把运行的机会让给其他的线程;

5、 Synchronized声明不会被继承,如果一个用synchronized修饰的方法被子类覆盖,那么子类中这个方法不在保持同步,除非用synchronized修饰。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值