前言
Java 多线程的常用方法基本分为: 获取当前线程的操作, 线程休眠 sleep()方法, 线程让步 yield()方法, 等待其他线程终止 join()方法, 线程停止的一系列方法.
一, 获取当前线程的操作
1, 获取当前线程: Thread.currentThread();
需要注意的是: 当一个线程 A 开启后, 调用其他线程类 B 的普通方法时, 此时的线程还是线程 A, 当一个线程 A 直接调用另一个线程类 B 的 run()方法, 就和调用普通方法没有区别.
举个栗子说明 run()和 start()有十分明显的区别
- package com.xiaoaxiao.test.thread_test.book_test;
- /**
- * Created by xiaoaxiao on 2019/7/16
- * Description: run()和 start()不一样!!!
- */
- class MyThread1 extends Thread{
- @Override
- public void run() {
- try {
- System.out.println("run threadName="+Thread.currentThread().getName()+"begin");
- Thread.sleep(2000);
- System.out.println("run threadName="+Thread.currentThread().getName()+"end");
- }
- catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class ThreadTest2 {
- public static void main(String[] args) {
- MyThread1 mt = new MyThread1();
- System.out.println("begin ="+System.currentTimeMillis());
- // mt.run();
- mt.start();
- System.out.println("end ="+System.currentTimeMillis());
- }
- }
若调用 mt.run(), 输出结果为:
- begin =1563329164153
- run threadName=main begin
- run threadName=main end
- end =1563329166155
若调用 mt.start(), 输出结果为:
- begin =1563329194123
- end =1563329194124
- run threadName=Thread-0 begin
- run threadName=Thread-0 end
2, 获取当前线程的名字: Thread.currentThread().getName()
而获取当前对象的名字(只在 Thread 的继承类中出现):this.getName()
3, 获取当前线程的唯一标识: Thread.currentThread().getId()
二, 线程休眠 sleep()方法 - 单位为毫秒(ms)
线程休眠是指 让当前线程暂缓执行, 等到了预计时间后再恢复执行. 线程休眠会立即交出 CPU, 但是不会释放锁.
sleep()的流程: 运行状态 ->sleep()->阻塞状态 ->sleep()的时间结束后 ->就绪状态 ->系统调度 ->运行状态.
虽然 sleep()在指定时间可以从运行状态 ->(阻塞状态)->就绪状态, 但是处于就绪状态时, 需要经过系统的调度才能到达运行状态, 具体的系统调度是随机的 (由 CPU 进行控制), 这就导致了每次 sleep() 的时间不相同(是有差异的).
sleep()可能会抛出 InterruptedException 受查异常, 需要对异常进行处理.
sleep()立即交出 CPU, 不会释放锁.
举个栗子
- package com.xiaoaxiao.test.thread_test;
- /**
- * Created by xiaoaxiao on 2019/7/12
- * Description: 测试 thread 常用的方法 --sleep,
- */
- class MyRunnable2 implements Runnable{
- @Override
- public void run() {
- for (int i=0;i<3;i++){
- System.out.println(Thread.currentThread().getName());
- // sleep()
- try {
- Thread.sleep(1000);
- }
- catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public class threadMethodTest1 {
- public static void main(String[] args) {
- MyRunnable2 myRunnable2 = new MyRunnable2();
- Thread thread1 = new Thread(myRunnable2,"A");
- // Thread thread2 = new Thread(myRunnable2,"hello");
- Thread thread2 = new Thread(myRunnable2,"B");
- Thread thread3 = new Thread(myRunnable2,"C");
- thread1.start();
- thread2.start();
- thread3.start();
- }
- }
输出结果:
B C A A C B B C A
三, 线程让步 yield()方法 -- 运行态 (running)-> 就绪态(runnable)
线程让步是指 暂停执行当前的线程对象, 并执行其他线程, yield()方法会让当前线程交出 CPU(不一定立即交出 CPU), 不会释放锁.
yield()方法无法控制具体交出 CPU 的时间, 并且 yield()方法只能让拥有相同优先级的线程有获取 CPU 的机会
yield()会交出 CPU(不一定立即交出), 不会释放锁.
举个栗子
- package com.xiaoaxiao.test.thread_test;
- /**
- * Created by xiaoaxiao on 2019/7/12
- * Description: 测试 thread 常用的方法 --yield
- */
- class MyRunnable2 implements Runnable{
- @Override
- public void run() {
- for (int i=0;i<10;i++){
- System.out.println(Thread.currentThread().getName());
- // yield()
- Thread.yield();
- }
- }
- }
- public class threadMethodTest1 {
- public static void main(String[] args) {
- MyRunnable2 myRunnable2 = new MyRunnable2();
- Thread thread1 = new Thread(myRunnable2,"A");
- Thread thread2 = new Thread(myRunnable2,"B");
- Thread thread3 = new Thread(myRunnable2,"C");
- thread1.start();
- thread2.start();
- thread3.start();
- }
- }
输出结果:
A A A B B C C C B
四, 等待其他线程终止 join()方法
等待其他线程终止是指外汇代理主线程等待子线程执行完成之后再结束.(主线程 (或者某个线程) 中若调用了子线程 (或者是另外一个线程) 的 join()方法就必须得等该子线程 run()方法结束, 主线程才能继续执行)
join()方法只是对 Object 提供的 wait()做了一层包装而已. (join 在内部使用 wait()方法进行等待), 执行 wait(long)方法后, 当前线程的锁会被释放, 其他线程就可以调用此线程中的同步方法了.
join()方法的执行流程与 sleep()类似: 运行状态 ->join()->阻塞状态 ->join()中断 ->就绪状态 ->系统调度 ->运行状态.
join()可能会抛出 InterruptedException 受查异常, 需要对异常进行处理.
join()会释放锁.
举个栗子
- package com.xiaoaxiao.test.thread_test;
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- /**
- * Created by xiaoaxiao on 2019/7/12
- * Description: 测试 thread 常用的方法 --join
- */
- class MyRunnable3 implements Runnable{
- @Override
- public void run() {
- try {
- System.out.println("主线程睡眠前时间");
- threadMethodTest2.printTime();
- Thread.sleep(1000);
- System.out.println(Thread.currentThread().getName());
- System.out.println("睡眠结束时间");
- threadMethodTest2.printTime();
- }
- catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class threadMethodTest2 {
- public static void main(String[] args) throws InterruptedException {
- MyRunnable3 myRunnable3 = new MyRunnable3();
- Thread threadA = new Thread(myRunnable3,"子线程 A");
- System.out.println("代码开始");
- threadA.start();
- // 调用子线程的 join 方法, 当主线程执行到这一步了,
- // 一定会等到子线程中所有内容全部执行完, 主线程才会继续往下执行
- threadA.join();
- System.out.println(Thread.currentThread().getName());
- System.out.println("代码结束");
- }
- public static void printTime(){
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String time = format.format(date);
- System.out.println(time);
- }
- }
输出结果为:
代码开始
主线程睡眠前时间
2019-09-27 16:56:26
子线程 A
睡眠结束时间
- 2019-09-27 16:56:27
- main
代码结束
还可以使用 join(long)设置最长等待时间 (单位: ms), 若主线程等待 long 秒后, 无论子线程会不会结束, 此时 join() 中断, 主线程进入就绪状态.
- class Join extends Thread{
- @Override
- public void run() {
- try {
- System.out.println("begin Timer="+System.currentTimeMillis());
- Thread.sleep(5000);
- System.out.println("end Timer="+System.currentTimeMillis());
- }
- catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public class JoinTest {
- public static void main(String[] args) {
- Thread thread = new Join();
- thread.start();
- try {
- thread.join(2000);
- System.out.println("main end Timer:"+System.currentTimeMillis());
- }
- catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- // 打印结果
- begin Timer=1569575039934
- end Timer=1569575041934
- main end Timer:1569575041934
五, 线程停止的一系列方法
1, 设置标记位 (flag) 停止线程 -- 推荐(好写)
在线程类内部设置一个标记位 flag 并由这个 flag 对线程类的执行进行控制, 在线程类的外部对 flag 进行修改, 从而实现外部对类内部的停止.
- package com.xiaoaxiao.test.thread_test;
- /**
- * Created by xiaoaxiao on 2019/7/12
- * Description: 测试 thread 常用的方法 -- 线程停止 - 设置标记符
- */
- class MyRunnable4 implements Runnable{
- // 设置一个标记符
- private Boolean flag = true;
- @Override
- public void run() {
- int i=1;
- // 将该标记符当做线程持续进行的条件
- while (flag){
- try {
- Thread.sleep(1000);
- }
- catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("这是第"+i+"次执行"+"线程名称为:"
- +Thread.currentThread().getName());
- i++;
- }
- }
- public void setFlag(Boolean flag) {
- this.flag = flag;
- }
- }
- public class threadMethodTest3 {
- public static void main(String[] args) throws InterruptedException {
- MyRunnable4 myRunnable4 = new MyRunnable4();
- Thread thread = new Thread(myRunnable4);
- thread.start();
- Thread.sleep(5000);
- // 主线程睡眠 5s 后将 flag 设置为 false,
- // 当子线程再次访问是, flag 已经变为 false
- myRunnable4.setFlag(false);
- // 调用子线程的 join, 让主线程等待子线程执行完成后再执行
- thread.join();
- System.out.println("代码结束");
- }
- }
2, 调用 Thread 类的 stop()方法强制停止线程, 该方法不安全, 已经被 Deprecated(废弃)了.
该方法之所以不安全主要是会造成数据的不一致.
- while(flag){
- // 按照第一种停止方式, 即便在 x=3 后, flag 变为了 false,
- //y 依旧能被赋值为 4
- x=3;
- y=4;
- }
- // 而如果在 x=3 后直接 stop(), 则 y 就不会被赋值为 4 了
3, 调用 Thread 类的 interrupt()方法 -- 系统设置标志位
1interrupt()方法只是将线程状态置为中断状态而已, 它不会中断一个正在运行的线程, 此方法只是给线程传递一个中断信号, 程序可以根据此信号来判断是否需要终止.(使用 isInterrupted()判断中断状态)
2当线程中使用 wait(),sleep(),join()导致此线程阻塞, 则 interrupt()会在线程中抛出 InterruptException, 并且将线程的中断状态由 true 置为 false.
PS: 无论是在 sleep()过程中, interrupt(), 还是在 interrupt()过程中, sleep(), 都会抛出 InterruptedException 异常, 并将 interrupt 中断状态置为 false(sleep()和 interrupt 只要相遇就会出异常, 其他两者同理)
3interrupt()中断线程
a)线程中没有 wait(),sleep(),join(), 调用 interrupt 只是将线程中断状态设置为 true.
b)线程中有 wait(),sleep(),join(), 调用 interrupt 会抛出 InterruptException 并将线程中断状态由 true 置为 false, 在 catch 块中捕获该异常, 然后退出
举个栗子, sleep()对 interrupt()的影响
- package com.xiaoaxiao.test.thread_test.book_test;
- /**
- * Created by xiaoaxiao on 2019/7/16
- * Description: sleep()对 Interrupt()的影响
- * 无论是在 sleep()过程中, interrupt()
- * 还是在 interrupt()过程中, sleep()
- * 都会抛出 InterruptedException 异常, 并将 interrupt 状态置为 false
- */
- class MyThread2 extends Thread{
- @Override
- public void run() {
- super.run();
- try {
- for (int i=0;i<1000000;i++){
- System.out.println("i="+(i+1));
- }
- System.out.println("run begin");
- Thread.sleep(200000);
- System.out.println("run end");
- }
- catch (InterruptedException e) {
- System.out.println("先停止, 再遇到 sleep,interrupt 状态为:"
- +this.isInterrupted());
- e.printStackTrace();
- }
- }
- }
- public class InterruptTest {
- public static void main(String[] args) {
- MyThread2 mt = new MyThread2();
- mt.start();
- mt.interrupt();
- System.out.println("end!");
- }
- }
写在最后
之前一直有粉丝一直私信我说需要一些学习资料和面试题; 故笔者最近整理了一份完整的 Java 面试题和视频学习资料, 需要的朋友可以点击下方传送门免费领取!
传送门
以下是部分资料截图
来源: http://www.jianshu.com/p/98f4aa62faa7