zoukankan      html  css  js  c++  java
  • Java多线程基础

    前言

    近期抽空在学习多线程技术,在图书馆借了一本书放在了家里看,在公司就找来了Java多线程核心技术来学习,现在就学习过程做的笔记写到了这里。笔记只是简单整理,没有细粒度的描述,估计也没有很强的逻辑在里面,只是把重要的基础知识点罗列了出来,以此来巩固下多线程基础吧。

    目标

    从这篇读书笔记中,我们可以大概学习到以下知识:

    • 线程的创建
    • 线程的启动
    • 如何使线程暂停
    • 如何使线程停止
    • 线程的优先级
    • 线程安全相关问题

    线程创建

    在Java的JDK开发包中,为我们提供了两种实现多线程编程的方式,一种是集成Thread类,另一种是实现Runnable接口。因为Java单继承的特性,为了满足各种业务的需要,可以通过实现Runnable的方式来创建线程,其实Thread类的具体实现也是实现Runnable接口的。

    public class Thread implements Runnable

    1、继承Thread类的方式

     1 public class MyThread01 extends Thread {
     2 
     3     @Override
     4     public void run() {
     5         System.out.println(Thread.currentThread().getName() + " is a new thread.");
     6     }
     7 
     8     public static void main(String[] args) {
     9         MyThread01 myThread01 = new MyThread01();
    10         myThread01.start();
    11     }
    12 }

    2、实现Runnable接口的方式

     1 public class MyThread02 implements Runnable {
     2 
     3     @Override
     4     public void run() {
     5         System.out.println("This is a thread implement Runnable:" 
     6                 + Thread.currentThread().getName());
     7     }
     8 
     9     public static void main(String[] args) {
    10        Runnable myThread02 = new MyThread02();
    11        Thread thread = new Thread(myThread02);
    12         thread.start();
    13     }
    14 }

    currentThread()方法

    该方法可返回代码段正在被哪个线程所调用的信息。我们可以通过下面的方法,来熟悉这个方法的使用。

     1 public class MyThread01 extends Thread {
     2 
     3     public MyThread01() {
     4         System.out.println("构造函数Current thread:" + Thread.currentThread().getName());
     5     }
     6 
     7     @Override
     8     public void run() {
     9         System.out.println("run方法:" + Thread.currentThread().getName());
    10     }
    11 
    12     public static void main(String[] args) {
    13         // main线程调用
    14         MyThread01 myThread01 = new MyThread01();
    15         // 独立线程,start方法才是启动一个线程
    16         myThread01.start();
    17         // main线程调用
    18         //myThread01.run();
    19     }
    20 }

    isAlive()方法

    该方法用于判断当前的线程是否处于活动状态,活动状态就是指线程已经启动且尚未停止,处于运行或者准备开始的状态,都被认为是活动状态。

     1 public class MyThread03 extends Thread {
     2 
     3     @Override
     4     public void run() {
     5         System.out.println("run=" + this.isAlive());
     6     }
     7 
     8     public static void main(String[] args) {
     9         MyThread03 thread03 = new MyThread03();
    10         // false
    11         System.out.println("begin == " + thread03.isAlive());
    12         // true
    13         thread03.start();
    14         // true
    15         System.out.println("end == " + thread03.isAlive());
    16     }
    17 
    18 }

    sleep()方法

    该方法用于让当前线程休眠一段时间,不释放当前对象持有的锁,方法会抛出异常所以需要声明捕获。

    getId()方法

    该方法用于取得线程的唯一标识。

     1 public class MyThread04{
     2 
     3     public static void main(String[] args) {
     4        Thread thread = Thread.currentThread();
     5         try {
     6             Thread.sleep(3000L);
     7         } catch (InterruptedException e) {
     8             e.printStackTrace();
     9         }
    10         System.out.println(thread.getName() + " -- " + thread.getId());
    11     }
    12 }

    停止线程

    停止一个线程可以使用Thread.stop()方法,但是最好不用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的(unsafe),而且是已被弃用作废的(deprecated)。如果强制使用stop()方法来停止一个线程,则可能使得一些清理性的工作得不到完成,另外一种情况就是对锁定的对象进行了“解锁”,致使程序流程错误,导致最终得不到同步的处理而出现数据不一致的问题,所以在后续的版本中将不可用或者不被支持。

    大多数时候停止一个线程使用Thread.interrupt()方法,尽管方法的名称是“停止,中止”的意思,但是这个方法不会终止一个正在运行的线程,还需要加入一个判断才会完成线程的停止。

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

    1. 使用退出标志使线程正常退出,也就是当run方法完成后线程终止。
    2. 使用stop方法,不推荐,因为这个方法可能会带来线程不可预料的结果。
    3. 使用interrupt方法中断线程。

    interrupt()方法并不会马上终止一个线程,而是给线程设置上一个停止的标记

    判断线程是否是停止状态

    • this.interrupted():判断当前线程是否已经终端,会撤销中断状态标记
    • this.isInterrupted():判断当前线程是否已经终端。

    如果线程抛出了InterruptedException异常并被捕获,那么线程的中断标记也会被清除。

     1 public class MyThread05 extends Thread {
     2 
     3     @Override
     4     public void run() {
     5         super.run();
     6         try {
     7             Thread.sleep(20000L);
     8         } catch (InterruptedException e) {
     9             // false == InterruptedException被catch之后,将会清除中断标记
    10             System.out.println("Thread status:" + this.isInterrupted());
    11             e.printStackTrace();
    12         }
    13         System.out.println("run start...");
    14     }
    15 
    16     public static void main(String[] args) {
    17        MyThread05 thread = new MyThread05();
    18        thread.start();
    19        thread.interrupt();
    20         //System.out.println(thread.isInterrupted());
    21     }
    22 }

    暂停线程

    暂停线程意味着线程还可以恢复运行,我们可以使用suspend()方法来暂停线程,使用resume()方法来恢复线程的执行。

    但这里有个坑,就是在持有锁的情况下访问公共资源时使用了suspend与resume方法,就会造成公共的同步对象锁被独占,使得其他的线程无法访问到公共的同步对象。

    suspend与resume方法和stop方法一样暴力,执行的结果也容易出现因为线程的暂停而导致数据不同步。

    yield()方法

    yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间,但是放弃的时间不确定,有可能刚刚放弃又马上获得CPU时间。

    线程的优先级

    在操作系统中,线程可以划分优先级,高优先级的线程可以得到更多CPU执行的机会,设置线程优先级有助于帮助“线程规划器”确定下一个可以选择执行的线程,线程的优先级默认都是5。

    1 public static void main(String[] args) {
    2    Runnable myThread02 = new MyThread02();
    3    Thread thread = new Thread(myThread02);
    4    thread.setPriority(Thread.MAX_PRIORITY);
    5     thread.start();
    6 }

    线程的优先级分为1~10级,如果在这个范围之外则抛异常,数值越大等级越高。其中Thread中内置了几个表示等级的常量:

    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

    线程的优先级具有传递性,比如A线程启动B线程,则B线程的优先级与A是一样的。

    线程优先级只会影响线程获得CPU执行时间的可能性,但是不能保证高优先级的一定都比低优先级的先执行。

    守护线程

    在Java线程中有两种线程,一种是用户线程,一种是守护线程。

    守护线程是一种特殊的线程,他的作用就是为其他非守护线程的运行提供便利服务,守护线程最典型的应用就是GC。当进程中不存在非守护线程时,则守护线程自动销毁。

     1 public class MyThread07 extends Thread {
     2 
     3     private int a = 0;
     4 
     5     @Override
     6     public void run() {
     7         try {
     8             while (true) {
     9                 System.out.println("Print:" + a++);
    10                 Thread.sleep(1000L);
    11             }
    12         } catch (InterruptedException e) {
    13             e.printStackTrace();
    14         }
    15     }
    16 
    17     public static void main(String[] args) {
    18         try {
    19             MyThread07 thread = new MyThread07();
    20             // 设置该线程为守护线程,必须启动线程之前设置
    21             thread.setDaemon(true);
    22             thread.start();
    23 
    24             Thread.sleep(5000L);
    25             System.out.println("用户线程到此就执行结束了,守护线程也会退出。。");
    26         } catch (InterruptedException e) {
    27             e.printStackTrace();
    28         }
    29     }
    30 }
    31 打印输出:
    32 Print:0
    33 Print:1
    34 Print:2
    35 Print:3
    36 Print:4
    37 用户线程到此就执行结束了。

    参考资料

    1、Java多线程编程核心技术 / 高洪岩著.—北京:机械工业出版社,2015.6

  • 相关阅读:
    Codeforces1420E. Battle Lemmings 题解 动态规划
    C++使用partial_sum求前缀和
    HDU6171 Admiral 题解 折半搜索
    HDU3017 Treasure Division 题解 折半搜索
    使用re.split 按标点+空格的一种分割办法
    实现CString的Format功能,支持跨平台
    转载一篇makefile,说的很详细
    Microsoft C++ 异常: std::system_error std::thread
    源码一样,运行结果不一致的解决办法
    记录一次阿里的电话面试
  • 原文地址:https://www.cnblogs.com/captainad/p/11320244.html
Copyright © 2011-2022 走看看