带你聊聊 Java 并发编程之线程基础

01、简介

百丈高楼平地起,要想学好多线程,首先还是的了解一下线程的基础,这边文章将带着大家来了解一下线程的基础知识。

02、线程的创建方式

实现 Runnable 接口

继承 Thread 类

实现 Callable 接口通过 FutureTask 包装器来创建线程

通过线程池创建线程

下面将用线程池和 Callable 的方式来创建线程

public class CallableDemo implements Callable<String> {

    @Override

    public String call() throws Exception {

        int a=1;

        int b=2;

        System. out .println(a+b);

        return "执行结果:"+(a+b);

    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建一个可重用固定线程数为1的线程池

        ExecutorService executorService = Executors.newFixedThreadPool (1);

        CallableDemo callableDemo=new CallableDemo();

        //执行线程,用future来接收线程的返回值

        Future<String> future = executorService.submit(callableDemo);

        //打印线程的返回值

        System. out .println(future.get());

        executorService.shutdown();

    }

}

执行结果

3

执行结果:3

03、线程的生命周期

NEW:初始状态,线程被构建,但是还没有调用 start 方法。

RUNNABLED:运行状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行中”。调用线程的 start() 方法使线程进入就绪状态。

BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了 CPU 使用权。比如访问 synchronized 关键字修饰的方法,没有获得对象锁。

Waiting :等待状态,比如调用 wait() 方法。

TIME_WAITING:超时等待状态,超时以后自动返回。比如调用 sleep(long millis) 方法

TERMINATED:终止状态,表示当前线程执行完毕。

看下源码:

public enum State {

        NEW,

        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;

}

04、线程的优先级

线程的最小优先级:1

线程的最大优先级:10

线程的默认优先级:5

通过调用 getPriority() 和 setPriority(int newPriority) 方法来获得和设置线程的优先级

看下源码:

/**

    * The minimum priority that a thread can have.

    */

    public final static int MIN_PRIORITY = 1;

    /**

    * The default priority that is assigned to a thread.

    */

    public final static int NORM_PRIORITY = 5;

    /**

    * The maximum priority that a thread can have.

    */

    public final static int MAX_PRIORITY = 10;

看下代码:

public class ThreadA extends Thread {

    public static void main(String[] args) {

        ThreadA a = new ThreadA();

        System.out.println(a.getPriority());//5

        a.setPriority(8);

        System.out.println(a.getPriority());//8

    }

}

线程优先级特性:

继承性:比如 A 线程启动 B 线程,则B线程的优先级与 A 是一样的。

规则性:高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。

随机性:优先级较高的线程不一定每一次都先执行完。

05、线程的停止

stop() 方法,这个方法已经标记为过时了,强制停止线程,相当于 kill -9。

interrupt() 方法,优雅的停止线程。告诉线程可以停止了,至于线程什么时候停止,取决于线程自身。

看下停止线程的代码:

public class InterruptDemo {

    private static int i ;

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {

            //默认情况下isInterrupted 返回 false、通过 thread.interrupt 变成了 true

            while (!Thread.currentThread().isInterrupted()) {

                i++;

            }

            System.out.println("Num:" + i);

        }, "interruptDemo");

        thread.start();

        TimeUnit.SECONDS.sleep(1);

        thread.interrupt(); //不加这句,thread线程不会停止

    }

}

看上面这段代码,主线程 main 方法调用 thread线程的 interrupt() 方法,就是告诉 thread 线程,你可以停止了(其实是将 thread 线程的一个属性设置为了 true ),然后 thread 线程通过 isInterrupted() 方法获取这个属性来判断是否设置为了 true。这里我再举一个例子来说明一下,

看代码:

public class ThreadDemo {

    private volatile static Boolean interrupt = false ;

    private static int i ;

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {

            while (!interrupt) {

                i++;

            }

            System.out.println("Num:" + i);

        }, "ThreadDemo");

        thread.start();

        TimeUnit.SECONDS.sleep(1);

        interrupt = true;

    }

}

是不是很相似,再简单总结一下:

当其他线程通过调用当前线程的 interrupt 方法,表示向当前线程打个招呼,告诉他可以中断线程的执行了,并不会立即中断线程,至于什么时候中断,取决于当前线程自己。

线程通过检查自身是否被中断来进行相应,可以通过 isInterrupted() 来判断是否被中断。

这种通过标识符来实现中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。

06、线程的复位

两种复位方式:

Thread.interrupted()

通过抛出 InterruptedException 的方式

然后了解一下什么是复位:

线程运行状态时 Thread.isInterrupted() 返回的线程状态是 false,然后调用 thread.interrupt() 中断线程 Thread.isInterrupted() 返回的线程状态是 true,最后调用 Thread.interrupted() 复位线程Thread.isInterrupted() 返回的线程状态是 false 或者抛出 InterruptedException 异常之前,线程会将状态设为 false。

下面来看下两种方式复位线程的代码,首先是 Thread.interrupted() 的方式复位代码:

public class InterruptDemo {

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {

            while (true) {

                //Thread.currentThread().isInterrupted()默认是false,当main方式执行thread.interrupt()时,状态改为true

                if (Thread.currentThread().isInterrupted()) {

                    System.out.println("before:" + Thread.currentThread().isInterrupted());//before:true

                    Thread.interrupted(); // 对线程进行复位,由 true 变成 false

                    System.out.println("after:" + Thread.currentThread().isInterrupted());//after:false

                }

            }

        }, "interruptDemo");

        thread.start();

        TimeUnit.SECONDS.sleep(1);

        thread.interrupt();

    }

}

抛出 InterruptedException 复位线程代码:

public class InterruptedExceptionDemo {

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {

            while (!Thread.currentThread().isInterrupted()) {

                try {

                    TimeUnit.SECONDS.sleep(1);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                    // break;

                }

            }

        }, "interruptDemo");

        thread.start();

        TimeUnit.SECONDS.sleep(1);

        thread.interrupt();

        System.out.println(thread.isInterrupted());

    }

}

结果:

false

java.lang.InterruptedException: sleep interrupted

at java.lang.Thread.sleep(Native Method)

at java.lang.Thread.sleep(Thread.java:340)

at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)

at com.cl.concurrentprogram.InterruptedExceptionDemo.lambda$main$0(InterruptedExceptionDemo.java:16)

at java.lang.Thread.run(Thread.java:748)

需要注意的是,InterruptedException 异常的抛出并不意味着线程必须终止,而是提醒当前线程有中断的操作发生,至于接下来怎么处理取决于线程本身,比如

直接捕获异常不做任何处理

将异常往外抛出

停止当前线程,并打印异常信息

像我上面的例子,如果抛出 InterruptedException 异常,我就break跳出循环让 thread 线程终止。

为什么要复位:

Thread.interrupted() 是属于当前线程的,是当前线程对外界中断信号的一个响应,表示自己已经得到了中断信号,但不会立刻中断自己,具体什么时候中断由自己决定,让外界知道在自身中断前,他的中断状态仍然是 false,这就是复位的原因。

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

推荐阅读更多精彩内容