JAVA-并发编程(三)

JAVA-并发编程(三)

sschrodinger

2019/5/16


线程


线程是现代操作系统的最小调度单位,也叫轻量级进程,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量。

线程状态

Java 线程在运行的生命周期中可能处于以下的 6 种不同状态,在给定的一个时刻,线程只能处于其中一个状态

状态名称 说明
NEW 初始状态,线程被构建,但是还没有调用start()方法
RUNNABLE 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中”
BLOCKED 阻塞状态,表示线程阻塞于锁
WAITING 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
TIMED_WAITING 超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的
TERMINATED 终止状态,表示当前线程已经执行完毕
image

note

  • 阻塞状态是线程在进入 synchronized 关键字修饰的的方法或代码块(获取锁)时的状态,但是阻塞在 java concurrnet 包中 Lock 接口的线程状态却是等待状态,因为 java.cincurrent 包中 Lock 接口对于阻塞的实现均使用了 LockSupport 类中相关的方法。

Daemon 线程

Daemon 线程是一种支持型线程,他主要被用作程序中后台调度以及支持性的工作。

note

  • 当一个 Java 虚拟机中不存在非 Daemon 线程的时候,java 虚拟机会自动退出。
  • 在构建 Daemon 线程时,不能依靠 finally 块中的内容来确保执行关闭或清理资源的逻辑,因为 Daemon 线程的 finally 语句块不一定一定被执行。

启动和终止线程


构造线程

在程序运行之前需要构造一个线程对象,线程对象在构造的时候需要提供线程所需要的属性,如线程所属的线程组、线程优先级、是否是 Daemon 线程等信息。

下面是 java 线程初始化的部分源码。

private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
    //当前线程就是父线程
    Thread parent = currentThread();
    this.group = g;
    //将Daemon、priority属性设置为父线程的对应属性
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    this.name = name.toCharArray();
    this.target = target;
    setPriority(priority);
    //将父线程的InheritableThreadLocal复制过来
    if (parent.inheritableThreadLocals != null) {
        this.inheritableThreadLocals = TheadLocal.createInheritedMap(parent.inheritableThreadLocals)
    }
    //分配一个线程ID
    tid = nextThreadID();
}

启动线程

调用 start() 方法。

中断

与操作系统的中断不同,java 的中断只是一个标志位,而不是去真正中断一个线程。

中断可以理解为线程的一个标志位属性,他表示一个运行中的线程是否被其他线程进行了中断操作(标志位置位)。其他线程调用该线程的 interrupt() 函数来对该线程进行置位。

线程通过检查自身是否被中断来进行响应,线程通过 isInterrupt() 来进行判断是否被中断。也可以调用静态方法 Thread.interrupt() 对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即是该线程被中断过,isInterrupt() 也会返回 false

许多申明抛出 InterruptException 的方法,在抛出 InterruptException 之前,Java 虚拟机会先将该线程的中断标识位清除,然后抛出 InterruptException,此时调用 isInterrpted() 方法也会返回 false

下面是 Interrupt 的例子:

public class Interrupted {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");
        sleepThread.setDaemon(true);
        Thread busyThread = new Thread(new BusyRunner(), "BusyThread");
        busyThread.setDaemon(true);
        sleepThread.start();
        busyThread.start();
        TimeUnit.SECONDS.sleep(5);
        sleepThread.interrupt();
        busyThread.interrupt();
        System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
        System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());
        SleepUtils.second(2);
        
    }
    
    static class SleepRunner implements Runnable {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                SleepUtils.second(10);
            }
        }
        
    }
    
    static class BusyRunner implements Runnable {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                
            }
        }
        
    }

}

//output:
//SleepThread interrupted is false
//BusyThread interrupted is true

suspend()、resume()、stop()

这是三个 java 不推荐的方法去暂停,恢复和停止线程。

note

  • 以suspend()为例,在调用后,县城不会释放已经占有的资源(比如锁),而是占着资源进入睡眠状态,这样容易引发死锁问题。
  • 同样,stop()方法在终结一个线程时不会保证线程的资源正常的释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序而可能工作在不确定状态下。
  • 暂停和恢复操作可以使用等待/通知机制来代替

安全的终止线程

利用中断或者一个boolean变量可以很方便的取消或者停止任务。

实例代码如下:

package test;

import java.util.concurrent.TimeUnit;

public class Shutdown {

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Runner one = new Runner();
        Thread countThread = new Thread(one, "CountThread");
        countThread.start();
        
        TimeUnit.SECONDS.sleep(1);
        countThread.interrupt();
        
        Runner two = new Runner();
        countThread = new Thread(two, "CountThread");
        countThread.start();
        TimeUnit.SECONDS.sleep(1);
        two.cancel();
    }
    
    private static class Runner implements Runnable {
        
        private long i;
        private volatile boolean on = true;

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("count i = " + i);
        };
        
        public void cancel() {
            on = false;
        }
        
    }

}

两种方式都是利用标志位跳出了while循环,以达到取消线程的任务。

Thread.join()

如果线程A执行了 thread.join() 语句,其含义是:当前线程A等待thread线程终止后才从 thread.join)() 返回。同时还提供了 thread.join(long)thread.join(long millis, int nanos) 两个超时方法。

如下代码是join的实例。

package test;

import java.util.concurrent.TimeUnit;

public class Join {
    
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Thread previous = Thread.currentThread();
        for (int i = 0; i < 10; i++) {
            //每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能够从等待中返回
            Thread thread = new Thread(new Domino(previous), String.valueOf(i));
            thread.start();
            previous = thread;
        }
        TimeUnit.SECONDS.sleep(5);
        System.out.println(Thread.currentThread().getName() + "\tterminate");
    }
    
    static class Domino implements Runnable {
        
        private Thread thread;
        
        public Domino(Thread thread) {
            // TODO Auto-generated constructor stub
            this.thread = thread;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                thread.join();
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
            System.out.println(Thread.currentThread().getName() + "\tterminate");
        }
        
    }

}

//output:
//main  terminate
//0 terminate
//1 terminate
//2 terminate
//3 terminate
//4 terminate
//5 terminate
//6 terminate
//7 terminate
//8 terminate
//9 terminate

实际上,join机制是利用了经典的等待/通知机制,现摘要部分源码如下:

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                //调用thread.join()会调用此方法
                //就是经典的步骤
                //获取对象的锁
                //如果条件不满足,那么调用对象的wait()方法,返回之后仍要检查条件
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

你可能感兴趣的:(JAVA-并发编程(三))