人生只若如初见——初见线程
2017-07-29 10:16
239 查看
之前在学习多线程的时候,那个时候并没有掌握得很好,用一句话来形容就是“略懂一二”。近段时间,又重温了多线程,我却发现,多线程的内容涉及甚广,不仅仅针对单一程序,而且还涉及有关系统的性能等方面,今天决定重新来了解它,都说“人生只若如初见”,总觉得初见的时候我们是最好的,至此,已初见的态度重新学习多线程。
谈到线程,首先还是得从一些基本只是开始——————
如图所示:
谈到线程,首先还是得从一些基本只是开始——————
线程和多线程
线程
所谓线程,使用官方的语言来说,能共享代码和数据空间的同一类程序,使用我自己的话来说,线程就如同一个系统中牵引着多个程序同时进行的“线”,(可能比喻得不太恰当,如谁有更形象的比喻,还请指正!),但是我们知道每个线程都有自己独立的的栈和程序计数器(PC),线程之间的切换开销相对于进程来说还是比较小的。那么,进程又是什么?没学过操作系统的人,可能不理解,其实,进程和线程如同双胞胎兄弟,从字面上区别,进程是指正在运行的程序,而线程范围似乎又包括进程,可是实质不以为然,进程可以是多种不同的程序。而线程是指同一类的程序,每一个进程都共享代码和数据空间,而线程是同一类线程共享代码和数据空间。从操作系统的角度来看,线程是CPU调度的最小单位,而进程是资源调度的最小单位。多线程
那么多线程是什么呢?从字面上看,是多个线程共同运行的状态,其实,这里强调的是多个线程同时并发的状态,而我们所面临的就是如何去解决这并发的问题,要想探究得深入,那么首先,多线程必须理解得非常的得透彻。理解
在了解线程是什么之后,需要明白一点,我们为什么要选择使用线程?很多人看来,使用线程能更大得提高效率,当然,也不能否认这点,多线程环境下,确实能够很大程度提高效率,但是凡事有利也有弊,有些时候,由于多线程创建和销毁过程是特别消耗内存的,因此这种情况下,效率不能提高反而降低性能。因此,对于多线程的使用,需要大量的经验来让我们学会如何掌控它。线程的几种状态以及状态切换
线程状态是线程控制的基础。要想更好的掌握如何适当得使用线程,那么其几个状态必须要掌握的,这也是线程的生命周期。如图所示:
线程定义的三种方式
继承Thread类
这种定义线程的方式是最简单的,但是一般不推荐。那这又是为什么呢?其实,这要看类与类之间的继承特点,我们应该都知道,类之间的继承属于单继承,而在以后的编码开发过程中,不能保证每一个类不能拥有父类,这样看来,如果再想使用继承Thread类的方式来定义线程,那么这样是行不通的。但是这种方式还是需要了解的,以下有个示例来让大家更好的理解。/** * 实现线程的方式一,继承Thread类 * Created by Cecilia on 2017/7/26. */ public class ThreadDemo extends Thread{ @Override public void run(){ for (int cnt=1;cnt<100;cnt++){ try { System.out.println("线程1计数:"+cnt); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { //第一种定义线程的方式 ThreadDemo td = new ThreadDemo(); td.start(); }
实现Runnable接口
实现Runnable接口的方式虽然在开启线程的过程中代码比较复杂,但是这种方式一般对于没有返回值的线程而言是最好的选择。因为接口具有多继承的特点,凭借这优点,在程序员定义线程时会优先选择这种方式。同样的,提供一个例子,让其更好地去理解。/** * 实现线程的方式二,实现Runnable接口 * Created by Cecilia on 2017/7/26. */ public class RunnableDemo implements Runnable{ @Override public void run(){ for (int cnt=1;cnt<100;cnt++){ try { System.out.println("线程2计数:"+cnt); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { //第二种定义线程的方式 RunnableDemo rd = new RunnableDemo(); Thread thread = new Thread(rd); thread.start(); }
实现Callable接口(带有返回值的线程)
前面两种方式是最常见的,但是对于大数据开发者来说,有很多场景是需要带有返回值的多线程,比如爬虫,这些需要将数据存储在Mon共DB中,同时需要利用多线程来使其爬取效率比较高。当然,以一个例子来让其看下这种定义方式。在开启线程时,需要使用Future工具类来帮助开启。/** * 第三种定义线程方式:实现带有返回值的线程接口(JDK1.5以后) * Created by Cecilia on 2017/7/26. */ public class CallableDemo implements Callable<String>{ @Override public String call() throws Exception { //Thread.sleep(1000); //Sleep能不用就不用,可以使用TimeUnit TimeUnit.MILLISECONDS.sleep(1000); String message = "This is another message!"; return message; } } public static void main(String[] args) { //第三种定义线程方式 CallableDemo callableDemo = new CallableDemo(); FutureTask<String> futureTask = new FutureTask<>(callableDemo); Thread thread1 = new Thread(futureTask); thread1.start(); while(true){ if (futureTask.isDone()){ //判断线程是否执行结束 try { System.out.println(futureTask.get()); //获取返回值,线程阻塞 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally { break; } }else{ try { TimeUnit.MILLISECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } }