黑马程序员_java多线程的一些总结(一)

来源:互联网 发布:淘宝卖家店铺装修视频 编辑:程序博客网 时间:2024/05/04 00:20
<span style="font-size: 14px; font-weight: bold; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------</span>

一、    进程和线程

1、   进程和线程的关系。

一般来说,我们运行一个应用程序的,就启动了一个进程,当然有些会启动多个进程。在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可能同时被多次执行。实际上,这样的代码块就是线程体。线程是进程中乱序执行的代码流程,也就是执行路径。对我们初学者来说,可以简单的先理解为进程包含线程,一个进程至少要包含一个线程,进程的执行,也就是进程中的线程在执行。

2、多线程存在的意义。

多进程实现了多任务并发执行,程序的多线程实现了进程的并发执行。并发执行,也就是我们现在打开电脑可以在敲代码的同时使用音乐播放     器听歌。这里的并发执行不是说计算机的CPU在同时在执行两个程序,而是很多程序的线程在轮流执行(至于执行的顺序就要涉及到调度算法),     然而,因为计算机的运行速度很快,让我们感觉到是同时在运行的。举个例子,绳子绑着一个球,快速甩圈下,会看到一个圆圈,其实我们知道球     不可能是一个圆圈,只是速度太快了眼睛跟不上。同理,计算机的速度是非常快的,在我们没反应过来,它已经做了很多事情了。

二、    线程的两种创建方式:

1、继承Thread类

步骤:①、定义类一个继承Thread

        ②、复写Thread中的run方法。

          ③、创建定义类的实例对象(相当于创建一个线程)。

          ④、用该对象调用线程的start方法(开启线程)

代码:

//步骤①,创建线程A,它继承自Thread类class ThreadA extends Thread{//步骤②,复写Thread类的run方法public void run(){//线程执行代码....}}class  ThreadTest{public static void main(String[] args) {ThreadA a1 = new ThreadA();//步骤③,<span style="font-size: 14px; line-height: 26px;">创建Thread类的实例对象</span>    <span style="white-space:pre"></span>ThreadA a2 = new ThreadA();a1.start();//开启一个线程   //步骤④,调用线程的start方法,开启线程a2.start();//开启第二线程//主线程中的的代码}}

2、实现Runnable接口

步骤:①、定义一个类实现Runnable的接口。

        ②、覆盖Runnable接口中的run方法

          ③、创建Runnable接口的子类的实例化对象。

          ④、通过Thread类创建线程对象,Runnable接口的子类对象作为实参传递给Thread类的构造方法

  ⑤、调用Thread类中start方法启动线程。

代码:

//步骤①,定义一个类实现Runnable的接口。class ThreadA implements Runnable{//步骤②,覆盖Runnable接口中的run方法。public void run(){//线程执行代码}}class  TicketDemo{public static void main(String[] args) {//步骤③,创建Runnable接口的子类的实例化对象。ThreadA t = new ThreadA();//步骤④,通过Thread类创建线程对象,将Runnable接口的子类对象作为实参传递给Thread类的构造方法。Thread t1 = new Thread(t);//创建了一个线程Thread t2 = new Thread(t);//创建了第二个线程//步骤⑤,调用Thread类中start方法启动线程。t1.start();t2.start();}}


三、    线程的两种创建方式的比较:

1、run方法

①、两种方法里都重写了run方法,首先一个线程需要有自己要运行的代码,run方法就是这些代码存放的地方
②、启动线程要调用Thread类的start方法,如果调用run方法,只是调用了一个普通的方法,需要执行完run方法中的代码,才开始执行后面的代码,程序中还是只有一个线程(java程序运行时,java虚拟机就会创建一个线程,该线程自动调用main方法)。其实调用start方法后,最终还是会调用run方法,我的理解是线程的运行需要操作系统的支持,因为线程启动后只是进入就绪状态没有开始运行,需要等待占领CPU的执行权。对于我们初学者,切记启动线程要调用start方法就好,入门后再去慢慢挖掘深层次的知识也是一种乐趣。

2、继承方式的缺点

java 是单继承的,如果一个类为了实现多线程继承了Thread类,这样该类就无法继承其他类了。但是实现接口就不会有这样的限制,可以实现多个接口,所以,在开发中我们一般会选择通过继承方式实现多线程、

3、接口实现的优点

①、适合多个相同的程序代码的线程去处理同一个资源

②、可以避免java中的单继承的限制

③、增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

四、线程的几种状态

前面我们说过主线程的main方法可以创建多个线程,而且这些线程的并发执行的,那么问题就来了,到底哪个线程先运行、线程运行是怎么切换的?
这里为了方便理解,先要懂得线程的几个状态:
①、创建状态:通过Thread类或其子类创建实例化对象后,线程已经创建完毕,但是没有启动,这时线程就处于创建状态,也可以叫初始状态。
②、就绪状态:线程调用start方法后,线程就处于就绪状态。当然就绪状态不仅是调用了start的线程,就绪状态就是所有满足运行条件的线程,但是还要等待CPU的资源,也就是所有有权利抢占CPU的线程,只要抢到CPU就可以运行。
③、运行状态:正在使用CPU的线程就处在运行状态,一般只有一个线程处于运行状态(单核计算机)。
④、阻塞状态:线程被迫睡眠(sleep),或由于要等待计算机的其他资源而没有强制CPU权利的线程(比如在正在等待打印机资源的线程)。
⑤、消亡状态:线程调用stop方法后就进入了消亡状态,线程执行完run方法后也会进入消亡状态。

这里简单的画图帮助理解:


注:①、线程调用sleep方法后会进入休眠状态,但是休眠结束后,并不能够马上执行,而是进入就绪状态,等待CPU资源。这里,提一点,sleep方法会发生异常,而且这里不能直接抛出异常,因为run方法复写了Runnable接口的方法,而接口中没有抛异常,所以sleep一抛异常就会出错,这里只能try、catch处理。

②、notify会唤醒进程池中的第一个线程,notifyAll会唤醒所用线程。


java程序启动后,主线程调用main方法,创建了很多子线程,这些子线程都处在创建状态,当这里线程调用start方法后,进入就绪状态,这时这些线程就有权利和主线程抢占CPU的资源(当然了,其他程序的线程也在抢CPU),谁抢到了CPU就可以执行一个时间片,所以我们在运行多线程时,可能会有多种不同的结果。

    


0 0