浅谈Java多线程(一)

一: 线程和进程

进程进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动。操作系统中,几乎所有运行中的任务对应一条进程,进程具有并发性,它可以同其他进程一起并发执行,按各自独立的、不可预知的速度向前推进.

(注意,并发性(concurrency)和并行性(parallel)是不同的。并行指的是同一时刻,多个指令在多台处理器上同时运行。并发指的是同一时刻只能有一条指令执行,但多个进程指令被被快速轮换执行,看起来就好像多个指令同时执行一样。)

线程: 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行.

归纳:一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少要包含一个线程.操作系统可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程.

二:线程的优势

1:进程之间不能共享内存,但线程之间共享内存非常容易.

2:系统创建进程时需要位该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高.

3:Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程.

三:线程的创建

在 Java 中实现多线程主要有两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口.

使用Thread类

1:定义Thread类的子类,并重写该类的run()方法.

2:创建Thread子类的实例.

3:调用线程对象的start()方法来启动该线程.

下面程序示范了通过THread类来创造并启动多线程

class MyThread extends Thread{  // 继承Thread类,作为线程的实现类 
    private String name ;       // 表示线程的名称 
    public MyThread(String name){ 
        this.name = name ;      // 通过构造方法配置name属性 
    } 
    public void run(){  // 覆写run()方法,作为线程的操作主体 
        for(int i=0;i<10;i++){ 
            System.out.println(name + "运行,i = " + i) ; 
        } 
    } 
} 
public class ThreadDemo02{ 
    public static void main(String args[]){ 
        MyThread mt1 = new MyThread("线程A ") ;    // 实例化对象 
        MyThread mt2 = new MyThread("线程B ") ;    // 实例化对象 
        mt1.start() ;   // 调用线程主体 
        mt2.start() ;   // 调用线程主体 
    } 
}

从程序可以看出,现在的两个线程对象是交错运行的,哪个线程对象抢到了 CPU 资源,哪个线程就可以运行,所以程序每次的运行结果肯定是不一样的.

使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量.

2:实现Runnable接口来创建线程类

1:定义Runnable接口的实现类,并重写该接口的run()方法.

2:创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象.

3:调用线程对象的start()方法来执行该线程.

下面程序示范了实现Runnable接口来创造并启动多线程

class MyThread implements Runnable{ // 实现Runnable接口,作为线程的实现类 
    private String name ;       // 表示线程的名称 
    public MyThread(String name){ 
        this.name = name ;      // 通过构造方法配置name属性 
    } 
    public void run(){  // 覆写run()方法,作为线程 的操作主体 
        for(int i=0;i<10;i++){ 
            System.out.println(name + "运行,i = " + i) ; 
        } 
    } 
} 
public class RunnableDemo01{ 
    public static void main(String args[]){ 
        MyThread mt1 = new MyThread("线程A ") ;    // 实例化对象 
        MyThread mt2 = new MyThread("线程B ") ;    // 实例化对象 
        Thread t1 = new Thread(mt1) ;       // 实例化Thread类对象 
        Thread t2 = new Thread(mt2) ;       // 实例化Thread类对象 
        t1.start() ;    // 启动多线程 
        t2.start() ;    // 启动多线程 
    } 
}

采用实现Runnable接口的方式创建多线程时,还可以继承其他类,所以一般推荐采用实现Runnable接口的方式来创建多线程.


线程共包括以下5种状态。
1. 
新建状态(New) : 线程对象被创建后,就进入了新建状态。此时它和其他Java对象一样,仅仅由Java虚拟机分配了内存,并初始化其成员变量值。

2. 就绪状态(Runnable)新建线程对象后,调用该线程的 start() 方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待 CPU 服务,这表明它已经具备了运行条件。

3. 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

4. 阻塞状态(Blocked):一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作,会让 CPU 暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(),suspend(),wait() 等方法,线程都将进入阻塞状态,发生阻塞时线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

5. 死亡状态(Dead): 线程执行完了、因异常退出了run()方法或者直接调用该线程的stop()方法(容易导致死锁,现在已经不推荐使用),该线程结束生命周期

四:控制线程

1、yield() 

yield()方法它能让当前线程暂停,但不会阻塞该线程,而是由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!

2、sleep()

sleep()方法声明抛出了InterrupedException异常。所以使用时,要么捕捉,要么声明抛出。

有2种重载方式:

static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。

static void sleep(long millis , int nanos):让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。但很少调用该方法.

sleep() 的作用是让当前线程休眠,即当前线程会从"运行状态"进入到"阻塞状态"。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。常用来暂停程序的运行。  

3、join()

join() 是Thread的一个实例方法。表示,当某个程序执行流中调用其他线程的join方法时,调用线程将被阻塞,直到被join的线程执行完毕。

有3种重载的形式:

join():等待被join的线程执行完成

join(long millis):等待被join的线程的时间最长为millis毫秒,若在millis毫秒内,被join的线程还未执行结束,则不等待。

join(long millis , int nanos):等待被join的线程的时间最长为millis毫秒加nanos微秒,若在此时间内,被join的线程还未执行结束,则不等待。很少使用这种形式.

即当前线程内,用某个线程对象调用join()后,会使当前线程等待,直到该线程对象的线程运行完毕,原线程才会继续运行.


猜你喜欢

转载自blog.csdn.net/qq_41430599/article/details/80778239