Java线程的概念:什么是线程?

  Windows 系统是多任务操作系统,它以进程为单位。

  系统可以分配给每个进程一段有限的执行 CPU 的时间(也称为 CPU 时间片

  单任务的特点就是排队执行,也就是同步,就像在 cmd 中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。这就是单任务环境的缺点,即 CPU 利用率大幅降低。




java 多线程单线程 java单线程和多线程_多线程


图2 单线程和多线程执行模式


  线程可以理解成是在进程中独立运行的子任务


Java多线程的实现方式

 实现多线程编程的方式主要有两种:一种是继承 Thread 类,另一种是实现 Runnable 接口。

继承 Thread 类

使用继承 Thread 类的方式实现多线程,最大的局限就是不支持多继承

  在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。

  除了异步调用之外,同步执行线程 start() 方法的顺序不代表线程启动的顺序。

实现 Runnable 接口

  如果要创建的线程类已经有一个父类,这时就不能再继承 Thread 类

提示:从 JDK 的 API 中可以发现,实质上 Thread 类实现了 Runnable 接口,其中的 run() 方法正是对 Runnable 接口中 run() 方法的具体实现。

  

java 多线程单线程 java单线程和多线程_Java_02


图1 使用Runnable接口启动线程流程

  要启动一个新的线程,不是直接调用 Thread  子类对象的 run() 方法,而是调用 Thread 子类的 start() 方法

 

两种方法的比较

1. 继承 Thread 类的优缺点
  1. Thread.currenThread().sleep();

而是可以简单地使用 Threadsleep() 方法,继承 Thread 类的方式使代码变得简单易读。

2. 实现 Runnable 接口的优缺点

  Thread 类是一个虚拟处理机严格的封装,因此只有当处理机模型修改或扩展时,才应该继承该类。由于 Java 技术只允许单一继承,因此如果已经继承了 Thread 类,就不能再继承其他任何类,这会使用户只能采用实现 Runnable 接口的方式创建线程。

Java线程的生命周期及线程的几种状态

  线程也具有生命周期,主要包括 7 种状态,分别是出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态,

下面对线程生命周期中的 7 种状态做说明。

  1. 出生状态:用户在创建线程时所处的状态,在用户使用该线程实例调用 start() 方法之前,线程都处于出生状态。
  2. 就绪状态:也称可执行状态,当用户调用 start() 方法之后,线程处于就绪状态。
  3. 运行状态:当线程得到系统资源后进入运行状态。
  4. 等待状态:当处于运行状态下的线程调用 Thread 类的 wait() 方法时,该线程就会进入等待状态。进入等待状态的线程必须调用 Thread 类的 notify() 方法才能被唤醒。notifyAll() 方法是将所有处于等待状态下的线程唤醒。
  5. 休眠状态:当线程调用 Thread 类中的 sleep() 方法时,则会进入休眠状态。
  6. 阻塞状态:如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时,线程进入就绪状态。对阻塞的线程来说,即使系统资源关闭,线程依然不能回到运行状态。
  7. 死亡状态:当线程的 run() 方法执行完毕,线程进入死亡状态。

 提示:一旦线程进入可执行状态,它会在就绪状态与运行状态下辗转,同时也可能进入等待状态、休眠状态、阻塞状态或死亡状态。

  

Java多线程之间访问实例变量

  当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能够拿到锁,那么这个线程就可以执行 synchronize 里面的代码。如果不能拿到锁,那么这个线程就会不断地尝试拿锁,直到能够拿到为止,而且有多个线程同时去争抢这把锁。

Java非线程安全问题的解决方法

非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。 

使用 synchronized 关键字修饰 doPost() 方法,即不允许多个线程同时修改 doPost() 方法中的变量。

Java多线程的同步机制:synchronized

  所谓同步机制,指的是两个线程同时作用在一个对象上,应该保持对象数据的统一性和整体性。Java 提供 synchronized 关键字,为防止资源冲突提供了内置支持。共享资源一般是文件、输入/输出端口或打印机。

  synchronized 不仅可以用到同步方法,也可以用到同步块。对于同步块,synchronized 获取的是参数中的对象锁。

  必须注意的是,Obj 对象的作用范围不同,控制情况也不尽相同

Java curentThread()方法的作用

  从结果可以发现,虽然 this.getName() 方法返回的都是 Thread-0,但是在构造方法中使用的即是 main 线程,而在 run() 方法中使用的是 A 线程。

Java isAlive()方法的作用

  isAlive() 方法的作用是判断当前的线程是否处于活动状态。

 

Java sleep方法的作用(sleep())

sleep() 方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)

Java getId()方法的作用

  getId() 方法的作用非常简单,就是取得正在运行线程的唯一标识

Java yieId()方法如何使用

yieId() 方法的作用是放弃当前的 CPU 资源,将它让给其他的任务去占用 CPU 执行时间。

Java停止(终止)线程详解版

停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。

在 Java 中有以下 3 种方法可以终止正在运行的线程:

  1. 使用退出标识,使线程正常退出,也就是当 run() 方法完成后线程终止。
  2. 使用 stop() 方法强行终止线程,但是不推荐使用这个方法,因为 stop() 和 suspend() 及 resume() 一样,都是作废过期的方法,使用它们可能产生不可预料的结果。
  3. 使用 interrupt() 方法中断线程。

停止不了的线程

 interrupt() 方法的作用是用来停止线程,但 intermpt() 方法的使用效果并不像循环结构中 break 语句那样,可以马上停止循环。

 

判断线程是不是停止状态

  1. this.interrupted():测试当前线程是否已经中断。
  2. this.islnterrupted():测试线程是否已经中断。

intermpted() 方法具有清除状态的功能,所以第二次调用 interrupted() 方法返回的值是 false。

异常法停止线程

  就可在线程中用 for 语句来判断线程是否为停止状态,如果是停止状态,则后面的代码不再运行。

在休眠中停止

  从运行结果来看,如果在休眠状态下停止某一线程则会拋出进入 InterruptedException 异常,所以会进入 catch 语句块清除停止状态值,使之变成 false。

强制停止线程

  调用 stop() 方法可以在任意情况下强制停止一个线程。

  注意:调用 stop() 方法时会抛出 java.lang.ThreadDeath 异常,但在通常情况下,此异常不需要显式地捕捉。

释放锁的不良后果

  使用 stop() 释放锁将会给数据造成不一致性的结果。

使用 return 停止线程

  除了上面介绍的方法外,还可以将 intermpt() 方法与 return 结合使用来实现停止线程的效果。

Java暂停/挂起线程(suspend())和恢复线程

(resume())

  在 Java 多线程中,可以使用 suspend() 方法暂停线程,使用 resume() 方法恢复线程的执行。

suspend() 与 resume() 方法

  从输出结果的时间来看,调用 suspend() 方法确实可以暂停线程,而在调用 resume() 方法后线程恢复运行状态。

独占问题

 在使用 suspend() 方法与 resume() 方法时,如果使用不当极易造成公共的同步对象被独占,从而使得其他线程无法访问公共同步对象。

不同步问题

  在使用 suspend() 方法与 resume() 方法时也容易出现因为线程的暂停而导致数据不同步的情况。

 

Java线程的优先级和执行顺序

优先级概述

在 Java 语言中,线程的优先级范围是 1~10,值必须在 1~10,否则会出现异常;优先级的默认值为 5。优先级较高的线程会被优先执行,当执行完毕,才会轮到优先级较低的线程执行。如果优先级相同,那么就采用轮流执行的方式。

  可以使用 Thread 类中的 setPriority() 方法来设置线程的优先级。语法如下:

  public final void setPriority(int newPriority)


  如果要获取当前线程的优先级,可以直接调用 getPriority() 方法。语法如下:


  public final int getPriority();


使用优先级