Java多线程:线程的实现、生命周期和优先级以及与进程之间的区别

什么是线程:
线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。

•一个进程可以包含一个或多个线程
•一个程序实现多个代码同时交替运行就需要产生多个线程
•CPU随机的抽出时间,让我们的程序一会做这件事情,一会做另外一件事情

什么是多线程:
多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务.

说明:
线程是程序内的顺序控制流,只能使用分配给程序的资源和环境。

多线程编程的目的:

多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

Java对多线程的支持

•同其他大多数编程语言不同,Java内置支持多线程编程(multithreaded programming)。多线程程序包含两条或两条以上并发运行的部分,把程序中每个这样的部分都叫作一个线程(thread)。每个线程都有独立的执行路径,因此多线程是多任务处理的一种特殊形式。

•多任务处理被所有的现代操作系统所支持。然而,多任务处理有两种截然不同的类型:基于进程的和基于线程的。

1.基于进程的多任务处理是更熟悉的形式。进程(process)本质上是一个执行的程序。因此基于进程的多任务处理的特点是允许你的计算机同时运行两个或更多的程序。举例来说,基于进程的多任务处理使你在运用文本编辑器的时候可以同时运行Java编译器。在基于进程的多任务处理中,程序是调度程序所分派的最小代码单位。
2.而在基于线程(thread-based)的多任务处理环境中,线程是最小的执行单位。这意味着一个程序可以同时执行两个或者多个任务的功能。例如,一个文本编辑器可以在打印的同时格式化文本

进程和线程的区别

•多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响.
•线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。

•多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配给它们独立的地址空间。进程间通信是昂贵和受限的。进程间的转换也是很需要花费的。另一方面,线程是轻量级的选手。它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。

•多线程可帮助你编写出CPU最大利用率的高效程序,使得空闲时间保持最低。这对Java运行的交互式的网络互连环境是至关重要的,因为空闲时间是公共的。例如,网络的数据传输速率远低于计算机处理能力,而本地文件系统资源的读写速度也远低于CPU的处理能力。当然,用户输入也比计算机慢很多。在传统的单线程环境中,程序必须等待每一个这样的任务完成以后才能执行下一步—尽管CPU有很多空闲时间。多线程使你能够获得并充分利用这些空闲时间。

Java线程模型

Java多线程的优点就在于取消了主循环/轮询机制。一个线程可以暂停而不影响程序的其他部分。

例如,当一个线程从网络读取数据或等待用户输入时产生的空闲时间可以被利用到其他地方。多线程允许活的循环在每一帧间隙中沉睡一秒而不暂停整个系统。

多线程的优势

•Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程。实际上,Java使用线程来使整个环境异步。这有利于通过防止CPU循环的浪费来减少无效部分。
•为更好地理解多线程环境的优势,我们可以将它与它的对照物相比较。单线程系统的处理途径是使用一种叫作轮询的事件循环方法。在该模型中,单线程控制在一无限循环中运行,轮询一个事件序列来决定下一步做什么。一旦轮询装置返回信号表明已准备好读取网络文件,事件循环调度控制管理到适当的事件处理程序。直到事件处理程序返回,系统中没有其他事件发生。这就浪费了CPU时间。这导致了程序的一部分独占了系统,阻止了其他事件的执行。总的来说,单线程环境,当一个线程因为等待资源时阻塞(block,挂起执行),整个程序停止运行。

多线程的实现

在Java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法。
1.继承Thread类并重写run方法。
2.通过定义实现Runnable接口的类进而实现run方法。

1.继承Thread类并重写run方法。
• Thread类:是专门用来创建线程和对线程进行操作的类。Thread中定义了许多方法对线程进行操作。
•Thread类在缺省情况下run方法什么都不做。可以通过继承Thread类并重写Thread类的run方法实现用户线程。

1 public class MyThread extends Thread {
2   public void run() {
3     … …
4   }
5 }
6 MyThread t = new MyThread();
7 t. start();

2.实现Runnable接口的类实现run方法。
通过建立一个实现了Runnable接口的类,并以它作为线程的目标对象来创建一个线程。
Runnable接口:定义了一个抽象方法run()。定义如下:

1 public interface java.lang.Runnable{
2     public abstract void run();
3 }
 1 2.实现Runnable接口的类实现run方法。
 2 创建的总体框架如下:
 3 .class MyRunner implements Runnable {
 4   public void run() {
 5      6   }
 7 }
 8 MyRunner r = new MyRunner();
 9 Thread t = new Thread( ThreadGroup group, Runnable target, String name);
10 例如: Thread t = new Thread( r, "aa");

总结:
1.两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。
2.在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继承了另一个类时,就应该用第二种方法来构造,即实现Runnable接口。
3.线程的消亡不能通过调用一个stop()命令。而是让run()方法自然结束。

线程的生命周期

线程的生命周期:一个线程从创建到消亡的过程。
线程的生命周期可分为四个状态:
1.创建状态
2.可运行状态
3.不可运行状态
4. 消亡状态

1.创建状态
当用new操作符创建一个新的线程对象时,该线程处于创建状态。
处于创建状态的线程只是一个空的线程对象,系统不为它分配资源

2. 可运行状态
执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。
这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。

3.运行状态

处于可运行状态的线程如果获得了cpu的资源,就会从可运行状态变为运行状态,执行run()方法中的任务。如果该线程失去cpu资源就会变成可运行状态,等待重新分配资源。也可以调用yield()方法主动让出cpu资源。

yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该Java线程同步已执行了足够的时间从而转到另一个线程。

4.阻塞状态
当发生下列事件时,处于运行状态的线程会转入到不可运行状态。
调用了sleep()方法
线程调用wait方法等待特定条件的满足
线程输入/输出阻塞

线程视图获得一个同步监视器,但更改同步监视器正被其他线程持有

程序调用了线程的suspend方法将线程挂起。但此方法容易造成死锁

返回可运行状态:
•处于睡眠状态的线程在指定的时间过去后
•如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变
•如果线程是因为输入/输出阻塞,等待输入/输出完成

5. 消亡状态
当线程的run方法执行结束后或强制性终止,该线程就会自然消亡。一旦死亡,不能复生。

线程的优先级

  1. 记住当线程的优先级没有指定时,所有线程都携带普通优先级。
  2. 优先级可以用从1到10的范围指定。10表示最高优先级,1表示最低优先级,5是普通优先级。
  3. 记住优先级最高的线程在执行时被给予优先。但是不能保证线程在启动时就进入运行状态。
  4. 与在线程池中等待运行机会的线程相比,当前正在运行的线程可能总是拥有更高的优先级。
  5. 由调度程序决定哪一个线程被执行。
  6. t.setPriority()用来设定线程的优先级。
  7. 记住在线程开始方法被调用之前,线程的优先级应该被设定。
  8. 你可以使用常量,如MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY来设定优先级

一个线程的优先级设置遵从以下原则:
1.线程创建时,子继承父的优先级
2.线程创建后,可通过调用setPriority()方法改变优先级。
3.线程的优先级是1-10之间的正整数。
  1 - MIN_PRIORITY,
  10 – MAX_PRIORITY
  5- NORM_PRIORITY

线程的调度策略

线程调度器选择优先级最高的线程运行。但是,如果发生以下情况,就会终止线程的运行。
•线程体中调用了yield()方法,让出了对CPU的占用权
•线程体中调用了sleep()方法, 使线程进入睡眠状态
•线程由于I/O操作而受阻塞
•另一个更高优先级的线程出现。
•在支持时间片的系统中,该线程的时间片用完。

Thread的一些常用方法
•测试 threads:
  isAlive()
• Thread priority:
  t getPriority()
  t setPriority()
•threads 进入非执行状态
  Thread. sleep()
  Thread. yield()

原文地址:https://www.cnblogs.com/2015110615L/p/6731916.html