博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java 多线程基础, 我觉得还是有必要看看的
阅读量:6452 次
发布时间:2019-06-23

本文共 7330 字,大约阅读时间需要 24 分钟。

Java 主线程名

我们启动的一个程序可以理解为一个进程, 一个进程中包含一个主线程, 线程可以理解为一个子任务. Java 中可以通过下面代码来获取默认的主线程名.

System.out.println(Thread.currentThread().getName());

运行结果为 main, 这是线程的名字并不是 main 方法, 通过此线程来执行 main 方法而已.

两种方式创建线程

1.继承 Thread 类

public class Thread1 extends Thread {    @Override    public void run() {        System.out.println("qwe");    }}

2.实现 Runnable 接口

public class Thread2 implements Runnable {    @Override    public void run() {        System.out.println("asd");    }}
Thread 实现
Runnable 接口. 并且多线程运行时, 代码的执行顺序与调用顺序是无关的. 另外如果多次调用
start方法则会抛出
java.lang.IllegalThreadStateException

currentThread 方法

返回当前代码正在被哪个线程调用.

public class Thread1 extends Thread {    public Thread1() {        System.out.println("构造方法的打印:" + Thread.currentThread().getName());    }    @Override    public void run() {        System.out.println("run 方法的打印:" + Thread.currentThread().getName());    }}
Thread1 thread1 = new Thread1();thread1.start();

isAlive 方法

判断当前线程是否处于活动状态.

public class Thread1 extends Thread {    @Override    public void run() {        System.out.println("run 方法的打印 Thread.currentThread().isAlive() == " + Thread.currentThread().isAlive());        System.out.println("run 方法的打印 this.isAlive() == " + this.isAlive());        System.out.println("run 方法的打印 Thread.currentThread().isAlive() == this.isAlive() == " + (Thread.currentThread() == this ? "true" : "false"));    }}
Thread1 thread1 = new Thread1();System.out.println("begin == " + thread1.isAlive());thread1.start();Thread.sleep(1000);System.out.println("end == " + thread1.isAlive());

执行结果如下

begin == falserun 方法的打印 Thread.currentThread().isAlive() == truerun 方法的打印 this.isAlive() == truerun 方法的打印 Thread.currentThread() == this == trueend == false

thread1 在 1秒之后执行完成. 所以输出结果为 false. 并且 Thread.currentThread()this 是同一个对象, 可以理解成执行当前 run 方法的线程对象就是我们自己(this).

如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动时, 运行结果和前面实例是有差异的. 造成这样的差异的原因还是来自于 Thread.currentThread()this 的差异.

System.out.println("begin == " + thread1.isAlive());//thread1.start();// 如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动Thread thread = new Thread(thread1);thread.start();Thread.sleep(1000);System.out.println("end == " + thread1.isAlive());

执行结果

begin == falserun 方法的打印 Thread.currentThread().isAlive() == truerun 方法的打印 this.isAlive() == falserun 方法的打印 Thread.currentThread() == this == falseend == false

Thread.currentThread().isAlive()true 的原因是因为, Thread.currentThread() 返回的是 thread 对象, 而我们也是通过此对象来启动线程的, 所以是在活动状态.

this.isAlive()false 就比较好理解了, 因为我们是通过 thread 对象启动的线程来执行 run 方法的. 所以它是 false. 同时也说明这两个不是同一个对象.

sleep 方法

在指定的毫秒数内让当前 "正在执行的线程" 休眠. "正在执行的线程" 只得是 Thread.currentThread() 返回的线程.

Thread.sleep(1000);

停止线程

停止一个线程意味着在线程处理完任务之前停掉正在做的操作, 也就是放弃当前的操作.

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

  1. 使用退出标志, 使线程正常退出, 也就是当 run 方法完成后线程终止.
  2. 使用 stop 方法强行终止线程, 但是不推荐使用这个方法.
  3. 使用 interrupt 方法中断线程.

停不了的线程

调用 interrupt 方法仅仅是当前线程打了一个停止标记, 并不是真正的停止线程.

public class Thread1 extends Thread {    @Override    public void run() {        for(int i = 0; i < 500000; i++) {            System.out.println(i);        }    }}
Thread1 thread1 = new Thread1();thread1.start();Thread.sleep(2000);thread1.interrupt();

我们两秒后调用 interrupt 方法, 根据打印结果我们可以看到线程并没有停止, 而是在执行完 500000 此循环后 run 方法结束线程停止.

判断线程是否是停止状态

我们将线程标记为停止后, 需要在线程内部判断一下这个线程是否是停止标记, 如果是则停止线程.

两种判断方法:

  1. Thread.interrupted(); 也可以使用 this.interrupted();
  2. this.isInterrupted();

下面是两个方法的源码:

public static boolean interrupted() {        return currentThread().isInterrupted(true);    }        public boolean isInterrupted() {        return isInterrupted(false);    }

interrupted() 方法数据静态方法, 也就是判断当前线程是否已经中断. isInterrupted() 判断线程是否已经被中断.

来自官网的
interrupted() 方法重点.
线程的 中断状态 由该方法清除. 换句话说, 如果连续两次调用该方法, 则第二次调用将返回 false (在第一次调用已清除了其中断状态之后, 且第二次调用检验完中断状态前, 当前线程再次中断的情况除外).

异常停止线程

public class Thread1 extends Thread {    @Override    public void run() {        try {            for (int i = 0; i < 500000; i++) {                if (this.isInterrupted()) {                    System.out.println("线程停止");                    throw new InterruptedException();                }                System.out.println("i = " + i);            }        } catch (InterruptedException e) {            System.out.println("线程通过 catch 停止");            e.printStackTrace();        }    }}
Thread1 thread1 = new Thread1();thread1.start();Thread.sleep(1000);thread1.interrupt();

输出结果, 这是最后几行:

i = 195173i = 195174i = 195175线程停止线程通过 catch 停止java.lang.InterruptedException    at Thread1.run(Thread1.java:9)
当然我们也可以将
throw new InterruptedException(); 换成
return. 都是一样可以结束线程的.

在沉睡中停止

如果线程调用 Thread.sleep() 方法使线程进行休眠, 这时我们调用 thread1.interrupt()后会抛出. InterruptedException 异常.

java.lang.InterruptedException: sleep interrupted    at java.lang.Thread.sleep(Native Method)    at Thread1.run(Thread1.java:8)

暴力停止线程

暴力停止线程可以使用 stop 方法, 但此方法已经过时并不推荐使用, 原因如下.

  1. 即刻抛出 ThreadDeath 异常, 在线程的run()方法内, 任何一点都有可能抛出ThreadDeath Error, 包括在 catch 或 finally 语句中. 也就是说代码不确定执行到哪一步就会抛出异常.
  2. 释放该线程所持有的所有的锁. 这可能会导致数据不一致性.
public class Thread1 extends Thread {    private String userName = "a";    private String pwd = "aa";    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;    }    public String getPwd() {        return pwd;    }    public void setPwd(String pwd) {        this.pwd = pwd;    }    @Override    public void run() {        this.userName = "b";        try {            Thread.sleep(100000);        } catch (InterruptedException e) {            e.printStackTrace();        }        this.pwd = "bb";    }}
Thread1 thread1 = new Thread1();thread1.start();Thread.sleep(1000);thread1.stop();System.out.println(thread1.getUserName() + "     " + thread1.getPwd());

输出结果为:

b     aa

我们在代码中然线程休眠 Thread.sleep(100000); 是为了模拟一些其它业务逻辑处理所用的时间, 在线程处理其它业务的时候, 我们调用 stop 方法来停止线程.

线程是被停止了也执行了 System.out.println(thread1.getUserName() + " " + thread1.getPwd()); 来帮我们输出结果, 但是 this.pwd = "bb"; 并没有被执行.

所以, 当调用了 stop 方法后, 线程无论执行到哪段代码, 线程就会立即退出, 并且会抛出 ThreadDeath 异常, 而且会释放所有锁, 从而导致数据不一致的情况.

interrupt 相比 stop 方法更可控, 而且可以保持数据一致, 当你的代码逻辑执行完一次, 下一次执行的时候, 才会去判断并退出线程.

如果大家不怎么理解推荐查看 这篇文章. 下面是另一个比较好的例子.

如果线程当前正持有锁(此线程可以执行代码), stop之后则会释放该锁. 由于此错误可能出现在很多地方, 那么这就让编程人员防不胜防, 极易造成对象状态的不一致. 例如, 对象 obj 中存放着一个范围值: 最小值low, 最大值high, 且low不得大于high, 这种关系由锁lock保护, 以避免并发时产生竞态条件而导致该关系失效.

假设当前low值是5, high值是10, 当线程t获取lock后, 将low值更新为了15, 此时被stop了, 真是糟糕, 如果没有捕获住stop导致的Error, low的值就为15, high还是10, 这导致它们之间的小于关系得不到保证, 也就是对象状态被破坏了!

如果在给low赋值的时候catch住stop导致的Error则可能使后面high变量的赋值继续, 但是谁也不知道Error会在哪条语句抛出, 如果对象状态之间的关系更复杂呢?这种方式几乎是无法维护的, 太复杂了!如果是中断操作, 它决计不会在执行low赋值的时候抛出错误, 这样程序对于对象状态一致性就是可控的.

suspend 与 resume 方法

用来暂停和恢复线程.

独占

public class PrintObject {    synchronized public void printString(){        System.out.println("begin");        if(Thread.currentThread().getName().equals("a")){            System.out.println("线程 a 被中断");            Thread.currentThread().suspend();        }        if(Thread.currentThread().getName().equals("b")){            System.out.println("线程 b 运行");        }        System.out.println("end");    }}
try{    PrintObject  pb = new PrintObject();    Thread thread1 = new Thread(pb::printString);    thread1.setName("a");    thread1.start();    thread1.sleep(1000);    Thread thread2 = new Thread(pb::printString);    thread2.setName("b");    thread2.start();}catch(InterruptedException e){ }

输出结果:

begin线程 a 被中断

当调用 Thread.currentThread().suspend(); 方法来暂停线程时, 锁并不会被释放, 所以造成了同步对象的独占.

转载地址:http://jogwo.baihongyu.com/

你可能感兴趣的文章
bzoj4589 Hard Nim
查看>>
JSON 请求太大,无法反序列化。
查看>>
mysql gen.ci_mysql cmake 参数详解
查看>>
pcdmis怎么导出模型_3D游戏模型提取、导入、导出教程
查看>>
pythonprint的使用方法_python print用法是什么
查看>>
libevent mysql php_linux+nginx+mysql+php的简单配置
查看>>
python爬虫周志_python爬虫第二周
查看>>
python语言迷宫游戏_python代码实现一个迷宫小游戏
查看>>
rpmbuild打包mysql5.7_关于rpmbuild打包mysql造成无法启用aio功能
查看>>
mongodb启动成功连不上_MongoDB无法连接/认证失败
查看>>
mysql myisam引擎_试题-说一下mysql的MyISAM引擎 - 拿OFFER
查看>>
mysql 单机 最高负载_haproxy(单机)+mysql集群负载均衡
查看>>
oldpassword mysql_mysql OLD_PASSWORD's problem | 2hei's site
查看>>
python grequests极限_在Python中利用grequests实现一个并发请求功能
查看>>
python通过sum函数实现1到n的和_Python定义函数实现累计求和操作
查看>>
python隐藏源代码_隐身 对 《Python源码剖析》 的评论 | 豆瓣阅读
查看>>
华南x79主板u盘装系统教程_华南x79主板怎么装win7系统|华南x79主板装win7及BIOS设置...
查看>>
结构化查询语句简称mysql_整理MySql常用查询语句
查看>>
java spring 数据库连接_Spring连接数据库的几种常用的方式
查看>>
java list 分组数量_java8 集合 多字段 分组 统计个数代码
查看>>