Java程序员福利——阿里架构师详解Java多线程常用方法的使用

前言

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()方法——系统设置标志位

  • ①interrupt()方法只是将线程状态置为中断状态而已,它不会中断一个正在运行的线程,此方法只是给线程传递一个中断信号,程序可以根据此信号来判断是否需要终止。(使用isInterrupted()判断中断状态)
  • ②当线程中使用wait()、sleep()、join()导致此线程阻塞,则interrupt()会在线程中抛出InterruptException,并且将线程的中断状态由true置为false。
    PS:无论是在sleep()过程中,interrupt(),还是在interrupt()过程中,sleep(),都会抛出InterruptedException异常,并将interrupt中断状态置为false(sleep()和interrupt只要相遇就会出异常,其他两者同理)
  • ③interrupt()中断线程
    • 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面试题和视频学习资料,需要的朋友可以点击下方传送门免费领取!

传送门

以下是部分资料截图

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

  • 此片文章主要总结的是Thread类及相关的基础概念和API,首先需要厘清线程调度中的几个基本概念: 一、线程调度的...
    千淘萬漉阅读 2,565评论 0 2
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,957评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,454评论 1 15
  • 林炳文Evankaka原创作品。转载自http://blog.csdn.net/evankaka 本文主要讲了ja...
    ccq_inori阅读 654评论 0 4
  • 快手 测试工程师 工作职责: 负责移动客户端的产品测试,保证产品质量 根据产品产品需求和用户场景,编写测试案例,完...
    TonyLan阅读 173评论 0 0