随着开发项目中业务功能的增加,必然某些功能会涉及到线程以及并发编程的知识点。笔者就在现在的公司接触到了很多软硬件结合和socket通讯的项目了,很多的功能运用到了串口通讯编程,串口通讯编程的安卓端就是基于线程的方式和硬件保持通讯的。
关于Java线程,先了解一下Java线程的生命周期和物种基本状态,先上一张经典的图
上图也比较直观的绘制了关于Java线程的生命周期同时也囊括了Java线程的重点知识点。
当线程对象创建后,线程即进入新建状态,如:Thread t = new MyThread();
当线程对象的start()方法(t.start();)调用时,此时线程进入就绪状态。处于就绪状态的线程只能说明线程此时已经做好准备,随时等待cpu的调度并不是说 t.start(): 后线程就会立马执行;
当cpu开始调度已经处于就绪状态的线程时,此时线程才真正的开始他的工作,即进入了运行状态。注意(敲黑板!):就绪状态是进入到运行状态的唯一入口。也就是说线程想进入运行状态,那线程就必须先处于就绪状态。
处于运行状态的线程处于某种原因呢,暂时放弃了对cpu的使用权,停止了执行,此时也就进入了阻塞状态,知道线程再次进入到就绪状态,才有机会被cpu调用进入到运行状态。而根据造成阻塞的原因不同,分为了一下三种阻塞:
-
等待阻塞:运行状态的线程执行到了wate()方法,使线程进入了等待阻塞状态。
-
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),他就会进入同步阻塞状态。
-
其他阻塞:通过线程的sleep()和join()或发出了I/O请求,线程进入到了阻塞状态。当sleep()超时、join()等待线程终止或超时、I/O处理完毕时,线程就会再次进入就绪状态。
线程执行完了或者在执行中因异常退出了run()方法,该线程就走完了他的一生了。
Java线程有三种常见的基本创建方式
方法
方法
不必想太多,这个run()方法同样是我们的线程执行体。重写完run方法后创建接口实例,并把该实例作为创建线程的target创建线程。没看懂没关系,直接上代码。
具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。有点绕~直接看代码吧~
这样我们就能清楚的看得出他们之间的关系。发现FutureTask类实际上是同时实现了Runnable和Future接口,使得他有了双重特性了,通过Runnable特性,可以作为Thread对象的target,而Future特性,使得其可以取得新创建线程中的call()方法的返回值。
301d5f65c91e7fe5966188e6d7.jpg" alt="" src="https://www.f2er.com/res/2019/03-16/23/bf857b301d5f65c91e7fe5966188e6d7.jpg" data-width="460" data-height="120">
原因在于通过ft.get()方法获取子线程call()方法的返回值时,当子线程此方法还未执行完毕,ft.get()方法会一直阻塞,直到call()方法执行完毕才能取到返回值。
上述主要讲解了三种常见的线程创建方式,对于线程的启动而言,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。
参考:
- 文章让你彻底征服多线程开发(一)