随着开发项目中业务功能的增加,必然某些功能会涉及到线程以及并发编程的知识点。笔者就在现在的公司接触到了很多软硬件结合和socket通讯的项目了,很多的功能运用到了串口通讯编程,串口通讯编程的安卓端就是基于线程的方式和硬件保持通讯的。

关于Java线程,先了解一下Java线程的生命周期和物种基本状态,先上一张经典的图

上图也比较直观的绘制了关于Java线程的生命周期同时也囊括了Java线程的重点知识点。

Java线程的五种状态:

新建状态(New):

当线程对象创建后,线程即进入新建状态,如:Thread t = new MyThread();

就绪状态(Runnable):

当线程对象的start()方法(t.start();)调用时,此时线程进入就绪状态。处于就绪状态的线程只能说明线程此时已经做好准备,随时等待CPU的调度并不是说 t.start(): 后线程就会立马执行;

运行状态(Running):

当CPU开始调度已经处于就绪状态的线程时,此时线程才真正的开始他的工作,即进入了运行状态。注意(敲黑板!):就绪状态是进入到运行状态的唯一入口。也就是说线程想进入运行状态,那线程就必须先处于就绪状态。

阻塞状态(Blocked):

处于运行状态的线程处于某种原因呢,暂时放弃了对CPU的使用权,停止了执行,此时也就进入了阻塞状态,知道线程再次进入到就绪状态,才有机会被CPU调用进入到运行状态。而根据造成阻塞的原因不同,分为了一下三种阻塞:

  • 等待阻塞:运行状态的线程执行到了wate()方法,使线程进入了等待阻塞状态。

  • 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),他就会进入同步阻塞状态。

  • 其他阻塞:通过线程的sleep()和join()或发出了I/O请求,线程进入到了阻塞状态。当sleep()超时、join()等待线程终止或超时、I/O处理完毕时,线程就会再次进入就绪状态。

死亡状态(Dead):

线程执行完了或者在执行中因异常退出了run()方法,该线程就走完了他的一生了。

Java多线程的创建和启动

Java线程有三种常见的基本创建方式

继承Thread类,重写其run()方法

 
啊,正如上述代码所示,通过继承Thread类重写其run()方法,定义了一个新的线程类MyThread,其中我们所重写的run()方法体内的代码就是线程需要完成的任务了,专业点的来说我们称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。

实现Runnable接口,并重写实现其run()方法

不必想太多,这个run()方法同样是我们的线程执行体。重写完run方法后创建接口实例,并把该实例作为创建线程的target创建线程。没看懂没关系,直接上代码。

 
其实跟第一种方法也差不多的。那么Thread和Runnable之间到底是什么关系呢?我们再来看一个例子:
 
看起来有点奇怪,其实同样是可以创建线程的。那么聪明敏锐的你肯定会在脑瓜里面有个提问:那此时线程到底是执行MyRunnable里面的执行体呢还是执行MyThread里面的执行体呢?答案是MyThread里的执行体被执行。因为Thread类本身也是实现了Runnable接口,而run()方法最先是在Runnable接口中定义的方法。
 
从上面的代码我们不难看出,当执行到Thread类中的run()方法时,会首先判断target是否存在,存在则执行target中的run()方法,也就是实现了Runnable接口并重写了run()方法的类中的run()方法。但是上述给到的列子中,由于多态的存在,根本就没有执行到Thread类中的run()方法,而是直接先执行了运行时类型即MyThread类中的run()方法。

用Callable和Future接口创建线程

具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。有点绕~直接看代码吧~

 
我们很容易看得出来,此时的线程执行体不再是run()方法了,取而代之的是call()方法,并且这个方法是有返回的。在创建新的线程时,是通过FutureTask来包装MyCallable对象,同时作为了Thread对象的target。那么看下FutureTask类的定义:

这样我们就能清楚的看得出他们之间的关系。发现FutureTask类实际上是同时实现了Runnable和Future接口,使得他有了双重特性了,通过Runnable特性,可以作为Thread对象的target,而Future特性,使得其可以取得新创建线程中的call()方法的返回值。

 
执行下此程序,我们发现sum = 4950永远都是最后输出的。而“主线程for循环执行完毕..”则很可能是在子线程循环中间输出。由CPU的线程调度机制,我们知道,“主线程for循环执行完毕..”的输出时机是没有任何问题的,那么为什么sum =4950会永远最后输出呢?

原因在于通过ft.get()方法获取子线程call()方法的返回值时,当子线程此方法还未执行完毕,ft.get()方法会一直阻塞,直到call()方法执行完毕才能取到返回值。

上述主要讲解了三种常见的线程创建方式,对于线程的启动而言,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。

线程的开启暂时就讲这么多了,后面的文章还会继续讲述Java线程之如何优雅的关闭线程。

参考:

Java多线程学习篇——线程的开启的更多相关文章

  1. Java多线程(学习篇)

    Java多线程:(学习篇) 1.什么是线程 2.线程状态 3.线程中断 4.线程交互 5.同步机制 6.锁机制 7.堵塞队列与堵塞栈 8.条件变量.原子量.线程池等 9.线性安全类和Callable与 ...

  2. Java多线程学习(二)---线程创建方式

    线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...

  3. Java多线程学习总结--线程概述及创建线程的方式(1)

    在Java开发中,多线程是很常用的,用得好的话,可以提高程序的性能. 首先先来看一下线程和进程的区别: 1,一个应用程序就是一个进程,一个进程中有一个或多个线程.一个进程至少要有一个主线程.线程可以看 ...

  4. Java多线程学习(三)---线程的生命周期

    线程生命周期 摘要: 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞 ...

  5. Java多线程学习之线程的取消与中断机制

    任务和线程的启动很容易.在大多数情况下我们都会让他们运行直到结束,或是让他们自行停止.但是,有时我们希望提前结束任务或是线程,可能是因为用户请求取消,或是线程在规定时间内没有结束,或是出现了一些问题迫 ...

  6. JAVA多线程学习十一-线程锁技术

    前面我们讲到了synchronized:那么这节就来将lock的功效. 一.locks相关类 锁相关的类都在包java.util.concurrent.locks下,有以下类和接口: |---Abst ...

  7. JAVA多线程学习七-线程池

    为什么用线程池 1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 例如: 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3 如果T1+T3> ...

  8. Java多线程学习之线程池源码详解

    0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...

  9. Java多线程学习总结--线程同步(2)

    线程同步是为了让多个线程在共享数据时,保持数据的一致性.举个例子,有两个人同时取钱,假设用户账户余额是1000,第一个用户取钱800,在第一个用户取钱的同时,第二个用户取钱600.银行规定,用户不允许 ...

随机推荐

  1. Java编程的逻辑 (44) - 剖析TreeSet

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  2. Java内存模型和JVM内存管理

    Java内存模型和JVM内存管理   一.Java内存模型: 1.主内存和工作内存(即是本地内存): Java内存模型的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取 ...

  3. oozie 客户端常用命令

    1.提交作业,作业进入PREP状态 oozie job -oozie http://localhost:11000/oozie -config job.properties -submit job: ...

  4. oozie 运行demo

    昨晚装好了oozie,能启动了,并且配置了mysql作为数据库,好了,今天要执行oozie自带的demo了,好家伙,一执行就报错!报错很多,就不一一列举了,就说我最后解决的方法吧. oozie job ...

  5. 在Linux下用make指令编译进度条程序。

    首先建立一个新的文件,touch progress_bar.c 运行该vim progress_bar.c命令.写进度条的程序. 写进一个进度条程序: #include<stdio.h> ...

  6. js 实现遮罩某一部分

    借用一下喜马拉雅的效果图,比较一下下面的遮罩层的高度:       我们可以看到它的遮罩层一直都是只遮住的下面.开始想着怎么用CSS实现,几经测试也没弄出来,如果正在看这篇文章的你,知道如何用CSS达 ...

  7. CSS3圆角,阴影,透明

    CSS实现圆角,阴影,透明的方法很多,传统的方法都比较复杂,用CSS3就方便很多了,虽然现在各浏览器对CSS3的支持还不是很好,但不久的将来CSS3就会普及. 1.圆角 CSS3实现圆角有两种方法. ...

  8. 9、Qt 事件处理机制

    原文地址:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...

  9. 输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。

    题目描述 输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数. 输入描述: 输入一个int型整数 输出描述: 按照从右向左的阅读顺序,返回一个不含重复数字的新的整数 输入例子 ...

  10. Android下ListView上下滑动背景变黑

    老问题,Google一下就能找到N多答案,为方便自己日后查阅,记录如下: 手指在ListView上下滚动时,ListViewItem背景变黑,因为在滚动的时候为了提升性能做了优化,为提高滚动的性能,A ...