Java多线程总结(一)

Java多线程概念及六大状态

Posted by Anriku on March 12, 2018

进程和线程

进程和线程的概念

现在的电脑手机,都能宏观(现在的多核处理器能在真正意义上实现这样的并行处理;但是我们的进程数是不会受处理器核数的影响的,可以比处理器的核心数多,那时候还是要进行时间片的分配,也就是宏观的实现并行处理)的在同一个时刻多个事情(比如说,边写博客边听歌呀!)。这是因为有多进程,才能让我们这么方便的做这些事情。简单的说:一个应用程序就可以看作是一个进程(当然会有一个应用有多个进程的情况)。

而一个进程(可以直接想象成一个应用程序)能够同时执行多个任务是由多线程来进行实现的(举个栗子:一个音乐播放器可以同时进行听歌和下载歌曲)。

进程和线程的区别

本质的区别在于一个进程拥有自己的一整套变量,而线程数据共享

由于线程是共享变量的,因此线程之间的通信比进程之间的通信更有效、更容易

一般的操作系统中,线程比进程更加的轻量,创建、撤销一个线程比启动新进程的开销小得多

Java中多线程的实现

Java多线程实现的两种方式

通过自己写一个继承自Thread类的线程类。例如下面这样:

class MyThread extends Thread {

    private int num = 5;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (num > 0) {
                System.out.println("ThreadId is:" + Thread.currentThread().getId() + ",num is " + num--);
            }
        }
    }
}

public class Test {

    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
    }
}

通过自己写一个实现了Runnable接口的Runnable类。例如下面这样:

class MyRunnable implements Runnable {

    private int num = 5;

    public void run() {
        for (int i = 0; i < 10; i++) {
            if (num > 0) {
                System.out.println("ThreadId is:" + Thread.currentThread().getId() + " ,num is " + num--);
            }
        }
    }
}

public class Test {

    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
}

PS:不要调用Thread类或者Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。

两种实现多线程方式的区别

通过实现Runnable接口的方式,可以让多个线程共享一个变量。而通过继承Thread类的方式不行。上面两个方式得到的结果如下:

Thread

Runnable

PS:通过继承Thread类的方式是不推荐的。因为我们应该将要并行运行的任务与运行机制解耦合。

线程的中断

线程中断的两种情况

  • 当线程的run方法执行方法体中最后一句后,并经由执行return语句返回时。
  • 出现了在方法体中没有捕获的异常时,线程将终止。PS:早期的Java版本中,有一个stop方法就是通过抛出ThreadDeath错误对象,由此杀死线程。但是这方法已经过时了,不要在代码中调用

线程中断的实现

  • 我们一般的线程中断是通过interrupt()和isInterrupted()两个方法结合来进行中断的。
  • 每个线程都有一个boolean变量用来标志一个线程是否被中断。interrupt()调用后此变量就为true。
  • 就像下面一样:
public class Test{
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = ()->{
            while (!Thread.currentThread().isInterrupted());
        };

        Thread thread = new Thread(runnable);
        thread.start();
        System.out.println(thread.isInterrupted());
        thread.interrupt();
        System.out.println(thread.isInterrupted());
    }
}

线程的六大状态

  • New(新创建)
    • 用new操作符创建一个新线程时,如new Thread(r)。此时线程处于New状态
  • Runnable(可运行)
    • 一旦调用start方法后,线程就处于Runnable状态(PS:一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供的运行时间)
  • Blocked(被阻塞)、Waiting(等待)和Timed waiting(计时等待)
    • 当一个线程试图获取一个内部的对象锁(不是java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程处于Block状态
    • 当线程等待另一个线程通知调度器一个条件时,它自己进入Waiting状态
    • 当Thread.sleep、Object.wait、Thread.join、Lock.tryLock以及Condition.await的计时版方法被调用时,线程处于Timed waiting状态
  • Terminated(被终止)
    • 因为run方法正常退出而自然死亡。
    • 因为一个没有捕获的异常终止run方法而意外死亡。

六大状态

线程的属性

线程优先级

  • 在默认的情况下,一个线程继承它的父类的优先级
  • 线程的优先级可以是MIN_PRIORITY(Thread类中定义为1)**与**MAX_PRIORITY(定义为10)之间的任意值。其中还有一个NORM_PRIORITY(在Thread类中定义为5)常量。
  • 但是,线程的优先级是高度依赖于系统的。当虚拟机依赖于宿主机平台的线程实现机制,Java线程的优先级映射到宿主机平台的优先级上,优先级的个数可能变多也可能变少。

守护线程

  • 线程对象通过调用setDaemon(true)方法将其设置为守护线程。
  • 守护线程的作用是为其它的线程提供服务。当程序中只有守护线程的时候,虚拟机就会退出
  • 由于守护线程的这个特点,因此守护线程应该永远不要去访问固有资源,如文件、数据库。

参考

  • Java核心技术 卷一

总结

  • 上面是对线程基本知识的介绍,也没啥好总结的。后面的内容继续介绍线程更深的东西。敬请期待~

转载请注明链接