java多线程的基础知识总结(附代码)
前言
本篇文章给大家带来的内容是关于java多线程的基础知识总结(附代码与资料),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
Java 主线程名
我们启动的一个程序可以理解为一个进程, 一个进程中包含一个主线程, 线程可以理解为一个子任务. Java 中可以通过下面代码来获取默认的主线程名.
1 |
System.out.println(Thread.currentThread().getName()); |
运行结果为 main, 这是线程的名字并不是 main 方法, 通过此线程来执行 main 方法而已.
两种方式创建线程
1.继承 Thread 类
1 2 3 4 5 6 |
public class Thread1 extends Thread { @Override public void run() { System.out.println( "qwe" ); } } |
2.实现 Runnable 接口
1 2 3 4 5 6 |
public class Thread2 implements Runnable { @Override public void run() { System.out.println( "asd" ); } } |
Thread 实现 Runnable 接口. 并且多线程运行时, 代码的执行顺序与调用顺序是无关的. 另外如果多次调用 start方法则会抛出 java.lang.IllegalThreadStateException
currentThread 方法
返回当前代码正在被哪个线程调用.
1 2 3 4 5 6 7 8 9 10 11 |
public class Thread1 extends Thread {
public Thread1() { System.out.println( "构造方法的打印:" + Thread.currentThread().getName()); }
@Override public void run() { System.out.println( "run 方法的打印:" + Thread.currentThread().getName()); } } |
1 2 |
Thread1 thread1 = new Thread1(); thread1.start(); |
为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜! 关注我,获取免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构视频学习资料以及电子书资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!
欢迎学习Java的朋友们加入Java高级进阶:524903398,Q群内已经上传了一系列的面试答案以及架构学习资料,同时工作以及项目上遇到的问题都可以在群内提出。
isAlive 方法
判断当前线程是否处于活动状态.
1 2 3 4 5 6 7 8 |
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" )); } } |
1 2 3 4 5 6 |
Thread1 thread1 = new Thread1();
System.out.println( "begin == " + thread1.isAlive()); thread1.start(); Thread.sleep(1000); System.out.println( "end == " + thread1.isAlive()); |
执行结果如下
1 2 3 4 5 |
begin == false run 方法的打印 Thread.currentThread().isAlive() == true run 方法的打印 this.isAlive() == true run 方法的打印 Thread.currentThread() == this == true end == false |
thread1 在 1秒之后执行完成. 所以输出结果为 false. 并且 Thread.currentThread() 和 this 是同一个对象, 可以理解成执行当前 run 方法的线程对象就是我们自己(this).
如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动时, 运行结果和前面实例是有差异的. 造成这样的差异的原因还是来自于 Thread.currentThread() 和 this 的差异.
1 2 3 4 5 6 7 8 |
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()); |
执行结果
1 2 3 4 5 |
begin == false run 方法的打印 Thread.currentThread().isAlive() == true run 方法的打印 this.isAlive() == false run 方法的打印 Thread.currentThread() == this == false end == false |
Thread.currentThread().isAlive() 是 true 的原因是因为, Thread.currentThread() 返回的是 thread 对象, 而我们也是通过此对象来启动线程的, 所以是在活动状态.
this.isAlive() 是 false 就比较好理解了, 因为我们是通过 thread 对象启动的线程来执行 run 方法的. 所以它是 false. 同时也说明这两个不是同一个对象.
sleep 方法
在指定的毫秒数内让当前 "正在执行的线程" 休眠. "正在执行的线程" 只得是
Thread.currentThread()返回的线程.
1 |
Thread.sleep(1000); |
停止线程
停止一个线程意味着在线程处理完任务之前停掉正在做的操作, 也就是放弃当前的操作.
在 Java 中有以下 3 种方法可以终止正在运行的线程:
-
使用退出标志, 使线程正常退出, 也就是当 run 方法完成后线程终止.
-
使用 stop 方法强行终止线程, 但是不推荐使用这个方法.
-
使用 interrupt 方法中断线程.
停不了的线程
调用 interrupt 方法仅仅是当前线程打了一个停止标记, 并不是真正的停止线程.
1 2 3 4 5 6 7 8 9 |
public class Thread1 extends Thread { @Override public void run() {
for (int i = 0; i < 500000; i++) { System.out.println(i); } } } |
1 2 3 4 |
Thread1 thread1 = new Thread1(); thread1.start(); Thread.sleep(2000); thread1.interrupt(); |
我们两秒后调用 interrupt 方法, 根据打印结果我们可以看到线程并没有停止, 而是在执行完 500000 此循环后 run 方法结束线程停止.
判断线程是否是停止状态
我们将线程标记为停止后, 需要在线程内部判断一下这个线程是否是停止标记, 如果是则停止线程.
两种判断方法:
-
Thread.interrupted(); 也可以使用 this.interrupted();
-
this.isInterrupted();
下面是两个方法的源码:
1 2 3 4 5 6 7 |
public static boolean interrupted() { return currentThread().isInterrupted(true); }
public boolean isInterrupted() { return isInterrupted(false); } |
interrupted()方法数据静态方法, 也就是判断当前线程是否已经中断.
isInterrupted()判断线程是否已经被中断.
来自官网的interrupted()方法重点.
线程的 中断状态 由该方法清除. 换句话说, 如果连续两次调用该方法, 则第二次调用将返回 false (在第一次调用已清除了其中断状态之后, 且第二次调用检验完中断状态前, 当前线程再次中断的情况除外).
异常停止线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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(); } } } |
1 2 3 4 |
Thread1 thread1 = new Thread1(); thread1.start(); Thread.sleep(1000); thread1.interrupt(); |
输出结果, 这是最后几行:
1 2 3 4 5 6 7 |
i = 195173 i = 195174 i = 195175 线程停止 线程通过 catch 停止 java.lang.InterruptedException at Thread1.run(Thread1.java:9) |
当然我们也可以将throw new InterruptedException();换成return. 都是一样可以结束线程的.
在沉睡中停止
如果线程调用
Thread.sleep()方法使线程进行休眠, 这时我们调用
thread1.interrupt()后会抛出.
InterruptedException异常.
1 2 3 |
java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at Thread1.run(Thread1.java:8) |
暴力停止线程
暴力停止线程可以使用
stop方法, 但此方法已经过时并不推荐使用, 原因如下.
-
即刻抛出 ThreadDeath 异常, 在线程的run()方法内, 任何一点都有可能抛出ThreadDeath Error, 包括在 catch 或 finally 语句中. 也就是说代码不确定执行到哪一步就会抛出异常.
-
释放该线程所持有的所有的锁. 这可能会导致数据不一致性.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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" ;
} } |
1 2 3 4 5 6 |
Thread1 thread1 = new Thread1(); thread1.start(); Thread.sleep(1000); thread1.stop();
System.out.println(thread1.getUserName() + " " + thread1.getPwd()); |
输出结果为:
1 |
b aa |
- 我们在代码中然线程休眠 Thread.sleep(1000 1d25c 00); 是为了模拟一些其它业务逻辑处理所用的时间, 在线程处理其它业务的时候, 我们调用 stop 方法来停止线程.
- 线程是被停止了也执行了 System.out.println(thread1.getUserName() + " " + thread1.getPwd()); 来帮我们输出结果, 但是 this.pwd = "bb"; 并没有被执行.
- 所以, 当调用了 stop 方法后, 线程无论执行到哪段代码, 线程就会立即退出, 并且会抛出 ThreadDeath 异常, 而且会释放所有锁, 从而导致数据不一致的情况.
- interrupt 相比 stop 方法更可控, 而且可以保持数据一致, 当你的代码逻辑执行完一次, 下一次执行的时候, 才会去判断并退出线程.
- 如果大家不怎么理解推荐查看 为什么不能使用Thread.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 方法
用来暂停和恢复线程.
独占
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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" ); } } |
1 2 3 4 5 6 7 8 9 10 11 |
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){ } |
输出结果:
1 2 |
begin 线程 a 被中断 |
当调用 Thread.currentThread().suspend(); 方法来暂停线程时, 锁并不会被释放, 所以造成了同步对象的独占.
以上就是java多线程的基础知识总结(附代码)的详细内容,更多请关注我要学编程网wyxbc.com其它相关文章!
- Java基础知识总结之多线程
- Java多线程基础知识总结笔记
- Java基础知识总结:多线程
- java我的总结——一些基础知识和代码
- Java多线程编程总结笔记——一多线程基础知识
- 黑马程序员 Java基础知识总结-多线程
- java多线程基础知识总结
- 黑马程序员学习log第四篇基础知识:JAVA的面向对象之多线程总结
- Java多线程编程总结笔记——02多线程基础知识
- Java多线程编程总结笔记——一多线程基础知识
- Java多线程基础知识总结笔记
- JAVA基础知识总结 01- 多线程
- Java多线程核心技术(一):基础知识总结
- java基础知识总结-多线程(二)
- JAVA_SE ----- 基础知识总结-----多线程
- java多线程基础知识:如何编写线程安全代码
- 基础知识《四》---Java多线程学习总结
- Java多线程基础知识回顾与总结;
- java基础知识总结(4)
- JAVA基础知识精华总结(一)