Java多线程基础

2019-08-16 12:10:47来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

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


原文链接:https://www.cnblogs.com/captainad/p/11320244.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:【java新特性】之方法引用与Lambda表达式

下一篇:二进制和位运算