(二)线程生命周期与Java关键字

进程与线程

进程是并发执行的程序在执行过程中分配和管理资源的基本单位。而进程是线程的容器,一个进程下可以有多个线程。

线程的生命周期

Thread的生命周期记录在内部的State中:

public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

1.新建线程与启动线程

Thread t = new Thread();
t.start();

2.终止线程

  • Thread.stop()方法可以终止线程,但是已经被标注为废弃方法,因为该方法会直接释放线程持有的锁,可能会造成锁维护的对象的不一致状态。
  • 安全的退出方法:可以在线程中监听一个状态值,需要退出时由线程自己按既定步骤退出。

3.线程中断

public void Thread.interrupted();  // 中断线程
public boolean Thread.isInterrupted();  // 判断线程是否被中断
public static boolean Thread.interrupted();  // 判断线程是否被中断,并清除当前中断状态

线程中断可以用来代替Thread.stop()方法实现主动停止线程。线程中断并不会使线程立刻退出,而是向线程发送一个中断通知,目标线程接到通知之后如何处理,完全由目标线程自己决定。

Thread t1 = new Thread() {
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("quit");
                break;
            }
        }
        Thread.yield();  // 让出自己的时间片给其他线程
    }
};

相对于之前的监听普通标记物的方法,中断命令由JDK提供支持,可以使Thread.sleep()Object.wait()等方法执行时抛出InterruptedException异常。因此更为泛用。(Thread.sleep()在抛出异常之后会清除中断状态,如果需要在捕捉到中断异常之后检测中断状态,还需要重新手动设置中断状态)

public static native void sleep(long millis) throws InterruptedException;
public final void wait() throws InterruptedException

4.等待(wait)和通知(notify)

public final void wait() throws InterruptedException
public final native void notify();
public final native void notifyAll();

wait()方法和notify()方法属于java.lang.Object对象。当在一个对象实例上调用wait()方法之后,当前线程就会在这个对象上等待。
如果在线程A中调用了obj.wait()方法,那么线程A就会停止继续执行,转为等待状态,直到其他线程调用了obj.notify()方法为止。相当于obj对象成为了线程间通信的媒介。
当一个线程调用了obj.wait()方法,那它就会进入obj对象的等待队列中,当obj.notify()方法被其他线程调用时,会唤醒等待队列中的一个线程,这个唤醒是不公平、完全随机的。如果调用的是obj.notifyAll()方法,则会唤醒等待队列中的所有线程。
想要使用obj.wait()obj.notify()方法,必须先使用synchronized获取obj对象,因为wait()notify()方法执行前需要先获得obj对象的一个监视器,执行之后再释放obj的监视器给其他等待中的线程。
Thread.sleep()Object.wait()方法都可以使线程等待若干时间,除了Object.wait()方法可以被唤醒外,Object.wait()方法还会释放obj的锁,而Thread.sleep()不会释放任何资源。

5.挂起(suspend)和继续执行(resume)

Thread.suspend()同样可以暂停线程,但是不会释放线程持有的资源,容易导致系统工作不正常,因此已被标为废弃。

6.等待线程结束(join)和谦让(yeild)

public final void join() throws InterruptedException  // 无限制等待
public final synchronized void join(long millis) throws InterruptedException  // 等待一段时间

当一个线程的执行依赖其他线程的执行结果时,就需要该线程等待目标线程执行结束后再继续执行。要做到这一点,可以使用wait()方法,而Java提供的join()方法同意可以做到。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread();
        t.start();
        t.join();
        System.out.println("Hello World");
    }
}

主线程调用t.join()方法,等t线程结束执行之后再继续执行。join()方法本质是让调用线程wait()方法在当前对象实例上。JDK中join()方法的实现核心片段如下:

while(isAlive()) {
    wait(0);
}

Thread.yield()方法可以使当前线程让出CPU资源,在这之后,该线程会立即加入之后的CPU资源争夺中,因此仍有可能继续执行。

public static native void yield();

Java关键字

1.volatile

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  • 禁止进行指令重排序。

因此,volatile关键字可以保证一个变量的可见性与一定程度上的有序性:

  • 当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
  • volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作

volatile关键字并不能保证操作的原子性,如果一个变量进行的操作是非原子性的,如自增(++),那么即使它是volatile变量,这个操作也是非原子性的。

2.synchronized关键字

关键字synchronized的作用是实现线程之间的同步。它的工作是对同步的代码加锁,使得每一次,只能有一个线程进入同步块,从而保证线程的安全性。

  • 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
  • 直接作用于实例方法:相当于对当前实例对象加锁,进入同步代码前要获得当前实例对象的锁。
  • 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

除了用于线程同步,确保线程安全外,关键字synchronized还可以保证线程之间的可见性和有序性。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 6,609评论 0 14
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 6,406评论 0 8
  • 相关概念 面向对象的三个特征 封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象. 多态的好处 允许不同类对...
    东经315度阅读 6,112评论 0 8
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 8,060评论 1 18
  • 生活在当今这个快节奏的社会中,我们每一个人就像是一个战士,每天早上从起床开始,就打算着如何为了自己理想去战斗、去拼...
    高桥美莎阅读 2,990评论 4 31