java多线程的那些事

引言

在平时的工作中,经常能碰到程序需要对大量的数据进行相同的操作,如果只是传统的单线运行,无疑会碰到执行时间过长。尤其是在处理一些即时性要求较高的数据的时候,处理太慢无疑就是事故级别的存在了。因此,这个时候,我们就可以考虑多线程。

为什么要使用多线程

没有什么问题是多线程解决不了的,如果有,就再起一个线程。

线程作为计算机调度资源的基本单位,使用多线程,可以充分利用CPU资源,避免由于某个任务执行时间过长而导致后续任务一直处于等待的情况。

线程的运行周期:

线程总共有五种状态:

线程的生命周期.png

其中:

  1. 阻塞存在三种情况:
  • 被阻塞: 常见于抢不到锁而被堵塞;
  • 等待: 线程等待另一个线程通知线程调度;
  • 计时等待: 线程只等待一定的时间,超时就不再等待;
  1. 线程死亡:
  • 自然终止: 当线程自动执行结束之后,线程资源被回收;
  • 异常终止: 线程被强行通过interrupt等操作中断;

那么在java中,如何新起一个线程呢?

java的多线程

1, Thread类;
2, Runnable接口
3, Future 和 Callable

接下来我们详细介绍下Thread类

Java多线程之Thread类详解(java.lang.Thread)

构造方法:
public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize, boolean inheritThreadLocals) {
        init(group, target, name, stackSize, null, inheritThreadLocals);
    }

参数的解析:

  1. group: 线程组,即new Thread 的该线程的ThreadGroup
  2. target: 一个Runnable的任务
  3. stackSize:该线程执行过程中的最大的栈深度;
  4. inheritThreadLocals : 是否继承原有父线程的变量,如果设置为true的话,会通过用一个ThreadLocalMap将变量存储;

start 方法

 public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

其中 threadStatus 表明线程的状态,各个值的含义为:

  • 0: NEW
  • 1: RUNNABLE
  • 2: BLOCKED
  • 3: WAITING
  • 4:TIMED_WAITING
  • 5: TERMINATED

start0() : 为一个native方法,该方法作用为产生新线程并调用run方法;

run方法:执行传入的Runnable实例中的run方法

public void run() {
        if (target != null) {
            target.run();
        }
    }

关于Thread类其他重要的方法

native方法:
  1. registerNative():将指定的方法,如start0,sleep,yield等本地方法绑定到函数中;
  2. sleep() :使该线程陷入阻塞的状态;
  3. yield():该方法在于将本身的状态改为就绪态;
  4. 备注: yield 和sleep 是有区别的yield是重新到就绪状态,而sleep是进入到阻塞的状态

其他非native方法

  • exit(): 调用 threadTerminated 方法,将其他属性都置为null
  • interrupt(): 通过(interrupt0)修改中断标识位,当出现阻塞的时候,线程就会抛出 InterruptedException;
  • join():调用的线程等待被调用实例线程一定时间(传参数)或者一直等待到结束;

下面给出一些简单的例子来进行调用:

public·class·MyThread·extends ·Thread {

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            public void run() {
                Long id = currentThread().getId();
                try {
                    System.out.println(getTime() +":开始的线程id:" + id);
                    Thread.sleep(5000);
                    System.out.println(getTime() + ":结束的线程id:" + id);
                } catch (InterruptedException exp) {
                    System.out.println(getTime() + ":中断的线程id:" + id);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {

                }
            }
        };
        MyThread testThread1 = new MyThread(runnable);
        testThread1.start();

        MyThread testThread2 = new MyThread(runnable);
        testThread2.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getTime()+":主线程执行完毕");
    }

    public MyThread(Runnable target) {
        super(target);
    }
    
    public static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(new Date());
    }
}

/**
执行的结果为:
开始的线程id:12
开始的线程id:13
主线程执行完毕
结束的线程id:13
结束的线程id:12
*/

interrupt方法尝试:

代码片段如下:
        testThread2.start();
        testThread1.interrupt();
        try {

输出结果如下:
2020-03-29 17:32:02:开始的线程id:13<br>
2020-03-29 17:32:02:开始的线程id:12<br>
2020-03-29 17:32:02:中断的线程id:12<br>
2020-03-29 17:32:03:主线程执行完毕 <br>
2020-03-29 17:32:07:结束的线程id:13<br>

分别尝试使用:join方法

  1. 添加 testThread1.join(1000); 执行结果如下:
代码片段如下:
            testThread1.join(1000);
            Thread.sleep(2000);
 2020-03-29 17:40:04:开始的线程id:13
 2020-03-29 17:40:04:开始的线程id:12
 2020-03-29 17:40:07:主线程执行完毕
 2020-03-29 17:40:09:结束的线程id:13
 2020-03-29 17:40:09:结束的线程id:12
  1. 添加 testThread2.join(); 执行结果如下:
代码片段如下:
            testThread2.join(1000);
            Thread.sleep(2000);
2020-03-29 17:41:00:开始的线程id:13
2020-03-29 17:41:00:开始的线程id:12
2020-03-29 17:41:05:结束的线程id:12
2020-03-29 17:41:05:结束的线程id:13
2020-03-29 17:41:07:主线程执行完毕

至此,我们可以看到,利用Thread新起一个线程是如此的简便。因此,下次碰到多任务的时候,不要再用线性执行的方式了,利用多线程的技术,让你的程序效率翻倍吧。

当然,使用多线程固然会加快速度,但是一味的增加线程数是否真的高效呢?其实不是的,期间涉及到线程的调度开销,以及多线程开发中存在着线程安全的问题。因此,如何优雅的利用多线程开发,期待下次文章吧。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 3,182评论 1赞 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,667评论 1赞 15
  • 林炳文Evankaka原创作品。转载自http://blog.csdn.net/evankaka 本文主要讲了ja...
    ccq_inori阅读 775评论 0赞 4
  • 一扩展javalangThread类二实现javalangRunnable接口三Thread和Runnable的区...
    和帅_db6a阅读 644评论 0赞 1
  • 此片文章主要总结的是Thread类及相关的基础概念和API,首先需要厘清线程调度中的几个基本概念: 一、线程调度的...
    千淘萬漉阅读 2,725评论 0赞 2

友情链接更多精彩内容