线程的概念:
我们在学习操作系统的时候都了解过线程的概念,线程是操作系统中最小的调度单元,也被叫做轻量级进程。每个线程都拥有自己的计数器,堆栈,局部变量,指令指针等属性,并且能够访问共享的内存变量。
Java从一开始就支持多线程,在Java中一个程序从main方法开始执行时就已经启动了线程,这个线程就是main线程。Java的线程实现是由Thread类和Runnable接口实现的,那么两者有什么区别呢。首先,我们可以看到Thread作为实现线程的类,可以被其他类继承然后重写run方法就可以实现一个线程,但是在Java中是不可以多重继承的,这就为我们的设计类时带来了很多限制,所以这个时候就可以考虑使用Runnable接口,然后通过Thread类实现线程。在实际应用中我们大部分的线程逻辑的具体实现也都是通过继承Runnable接口实现的。具体实现可以参考一下代码:
class MyThread extends Thread{
@Override
public void run(){
//do something...
}
}
class MyThreadRunnable implements Runnable{
@Override
public void run(){
//do something...
}
}
new MyThread().start();
new Thread(new MyThreadRunnable()).start();
Java线程特性:
线程的优先级:在Java线程中,有一个priority成员变量是控制线程的优先级的,我们可以通过函数setPriority(int priority)设置线程的优先级(默认的优先级是5)。但要注意的是在不同的JVM和操作系统中会有不同的结果,有些环境下,并不会根据线程的优先级来执行线程。所以在编程中我们尽量不要依赖优先级区控制线程的执行顺序。
线程的状态:在Java的Thread类中定义了一个枚举类型State,其中有Java线程的6种状态,即NEW(初始化),RUNNABLE(运行),BLOCKED(阻塞),WAITING(等待),TIMED_WAITING(超时等待),TERMINATED(终止)。下图就是线程状态之间的转换,在Java线程中比我们在操作系统中学习的线程多了一个超时等待状态,这个状态就是说当在等待一定时间后,超过了等待时间的限制,线程就会返回到运行状态。
Daemon线程:Daemon线程是一种支持型线程,是守护线程。它主要被用于后台调度以及支持性工作,当Java中不存在非Daemon线程是,Java虚拟机将会退出,同时Daemon线程也会结束。可以通过setDeamon(boolean deamon)方法设置一个线程是否为Daemon线程。
Java线程的操作:
线程的启动和终止:在文章的开头我们讲了Thread和Runnable的用法和区别,其中调用start()方法就是启动线程,当线程的run()方法结束时,线程也随之结束。
线程的中断:中断即一个线程可以被其他线程对它的操作进行终止,在Java中,可以通过调用interrupt()或调用静态方法Thread.interrupted()对某个线程进行中断,当一个线程被中断后,可以通过isInterrupted()方法来判断线程是否被中断了。但是,在线程已经被中断或者抛出了InterruptedException异常时,此时调用isInterrupted()方法依然会返回false。
线程的终止:当我们想要终止一个长期运行的线程时,我们应当怎么做?在Java的Thread类的API中有一个stop(),所以当我们调用stop()方法时,就是终止一个线程。但是调用stop()方法不能保证线程的资源得到释放,,所以在现有版本的Java中stop()方法已经过期。对于此种情况,我们可以设置相应的状态位来维护一个线程是否要进行下去。
Java线程间通信:在上一篇java内存模型的博客中,我们知道线程之间可以通过共享内存变量来进行通信。但当要保证线程之间的数据同步时,我们需要对数据进行上锁,这里就要用到volatile和synchronized关键字,同时,它们也担负起了线程之间进行通信的任务,当对数据进行读写是,每个线程都要得知是否另外的线程在操作数据,即对数据获取锁或者释放锁。
Java线程join()的使用:在Java线程中的join()方法的含义就是当一个线程调用了另外一个线程的join()方法时,则此线程需要等待另外一个线程终止后才能返回。我们在查看join()源码的时候可以发现在join()方法中调用了wait()方法,从而导致调用join()方法的线程阻塞。当线程结束时,就会从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()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
Java线程等待通知机制:
线程的等待通知机制和我们所熟知的生产者消费者模式异曲同工,基本原理就是当一个线程更改了一个与其他线程共享的资源是,要让另外的线程感知到变化。有一种思路就是一个线程通过在内部不断的轮询用来检查资源是否变化,但都过while(true)方式不断的轮询会极大的浪费系统资源。所以还可以通过另外一种更好的方式。就是通过wait()和notifyAll()或notify()方法实现线程之间的通信,从而完成资源的信息传递,具体可以参考代码:
class Main{
boolean flag = true;
Object lock = new Object();
public static void main(String[] args){
Thread wait = new Thread(new Wait());
wait.start();
TimeUnit.SECONDS.sleep(2);
Thread notify = new Thread(new Notify());
notify.start();
}
class Wait implements Runnable{
public void run(){
synchronized(lock){
while(flag){
System.out.println("wait");
lock.wait();
}
System.out.println("continnue");
}
}
class Notify implements Runnable{
public void run(){
synchronized(lock){
System.out.println("notify");
lock.notify();
System.out.println("completed");
}
}
}