- 概述
程序:程序是对数据描述与操作的代码的集合,如暴风语音应用程序
进程:是程序的一次动态执行过程,它对应了从代码加载,执行至执行完毕的一个完整过程。
线程:线程是进程中的一个执行流程,一个进程中可以运行多个线程(线程是进程中执行运算的最小单位)
- 进程
操作系统进程:系统进程(系统建立);用户进程(用户建立)
特点:
- 进程是系统运行程序的基本单位
- 每个进程都有自己独立的一块内存空间,一组系统资源
- 每一个进程的内部数据和状态都是完全独立的
动态性:执行过程,动态产生,动态消亡
并发性:任何进程可以一起执行
独立性:进程是一个能独立运行的进本单位,同时也是系统分配资源和调度的独立单位
异步性:进程之间相互制约,是进程具有执行的间断性,即进程按各自独立的不可预知的速度向前推进
- 线程
线程是进程中执行的运算的最小单位,可完成一个独立的顺序控制流程。每个进程中,必须至少建立一个线程
(主线程),来作为这个程序运行的入口点。如果在一个进程中同时运行了多个线程,用来完成不同的工作。则称为”多线程”;多个线程并不是同时执行的,是因为不同线程之间切换的时间非常短
多线程的好处
- 充分利用CPU资源
- 简化编译模型
- 带来良好的用户体验
- 进程与线程的区别
进程和线程的主要差别在于他们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,
在保护模式下,不会对其他进程产生影响,而线程只是一个进程中的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有独立的地址空间,一个线程死掉,就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源大,效率差,但是对于一些要求同时进行并且又要共享一些变量的并发操作,只能用线程,不能用进程
- Thread类常用用法
Thread:分配新的线程对象
Thread(Runnable target):配新的线程对象,target为run方法被调用的对象
Thread(Runnable target,String name):配新的线程对象,target为run方法被调用的对象,name为新线程的名称:
Void run():执行任务操作的方法
Void statrt():使该线程开始执行,Java虚拟机调用该线程的run()方法、
Void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
String getName():返回线程的名称
Void setName():更改线程名称
Int getPriority():返回线程的优先级
Void setPriority(int newPriority):更改线程的优先级
Static Thread currentThread():返回当前正在执行的线程对象的引用
Boolean isAlive():测试线程是否处于活动状态
Void join():等待该线程中止
Void interrupt():中断线程
Void yield():暂停当前正在执行的线程对象,并执行其他线程
- Java主线程
在Java程序启动时,一个线程立刻运行,该线程通常称为程序的主线程
Java的main方法是主线程的入口,每个进程都至少有一个主线程。它是程序开始时就执行的
尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制。
例子:
Thread thread=Thread.currentThread();
System.out.println("当前的线程是:"+thread.getName());
thread.setName("ljl");
System.out.println("当前的线程是:"+thread.getName());
作用:
(1)产生其他子线程的线程
(2)通常他必须最后完成执行,因为它执行各种关闭动作
使用:
- 定义一个线程,同时指明这个线程所要执行的代码,即期望完成的功能
- 创建线程对象
- 启动线程
- 中止线程
7.实现线程的两种方式
(1)继承Thread类创建线程
(2)实现Runnable接口创建线程
8.继承Thread类创建线程
使用此方法创建线程类,此线程类需要继承Thread类并重写Thread类的run()方法,因为Thread类的run()
方法是线程要执行操作任务的方法,所以线程要执行的操作代码都需要写在run()方法中,并通过调用
start()方法启动线程后调用
步骤:
- 定义要使用的线程类,继承Thread类
- 重写run方法,在run方法中编写代码
- 创建线程对象,调用start()方法启动线程
在创建线程未指定线程名的时候,自定义线程默认名为”Thread'-”加序号,序号为从0开始的整数
已经启动的线程的对象不能再次调用start()方法,否则会抛出IllegalThreadStateException异常
例子:
public class MyThread
{
public void run(){
//代码
}
}
public class test()
{
pulic static void mian(String[] arg)
{
MyThread mythread=new MyThread ();
Mythread.start();
}
}
- 线程实例调用start()方法和直接调用run()方法
(1)用start()方法来启动线程是实现了多线程,通过调用Thread类的start()方法来启动一个线程,这时此程序就处于就绪可运行状态,但是并没有运行,一旦得到cpu时间片,就开始执行运行run()方法。但要注意的是,此时需要等待run()方法执行完毕,即可继续执行下面的代码
(2)直接调用run()方法并没有实现多线程,run()方法只是类的一个普通方法而已,如果直接调用run()方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后菜可以继续执行下面的代码
注意:多个线程对象调用start()方法启动后,各自执行run()方法中的代码,相当于每个线程有自己的
独立的执行路径,多个分支相互不影响,并行执行,但由于CPU在一个时间点只能执行一个线程,因此多个 线程是交替执行的,获得CPU时间片的线程即刻执行,当前时间片执行完毕后,CPU就会获得下一个时间片的线程。分配的时间片长度不是完全一致的,可多可少,因此每次运行结果有所不同,总之是轮换交替执行的
结论:
start():启动线程 run():调用实例方法
- 实现Runnable接口创建线程(解决Java单继承)
Runnable接口定义在java.lang包中,其中声明了一个抽象方法run(),即public void run()。一个类
可以通过实现Runnable接口并实现其run()方法完成线程的所有的所有活动,已实现run()方法称为该线程对象的线程体。任何实现Runnable接口的对象都可以作为线程的目标对象。
步骤:
- 定义MyRunnable类实现接口Runnable接口,并实现run()方法,在run()方法中编写代码
- 创建MyRunnable类对象myRunnable
- 创建一个Thread类的对象myThread,将myRunnable对象作为参数传入
- 调用myThread对象的start()方法启动线程
代码:
Public class MyRunnable implements Runnable
{
Public void run()
{
}
}
- MyRunnable myRunnable=new MyRunnable();
Thread myThread=new Thread(myRunnable);
MyRunnable.start();
(2)Thread myThread=new Thread(new MyRunnable());
MyRunnable.start();
- 两种创建线程方式的应用
- 继承Thread类的方式,编写简单,可以直接操作线程,适用于单继承的情况
- 当一个线程继承继承了另一个类时,就只能实现Runnable接口的方式来创建线程,而且这种方式还可以实现多个线程之间的资源共享。
- 线程的状态
- 创建状态
在程序中创建了一个线程对象后,新的线程对象就处于创建状态。此时,它已经获得了相应的资源,但还没有处于可运行的状态,这时可以通过Thread类的方法来设置线程对象的属性
- 就绪状态
线程创建之后,就可以通过调用start()方法启动线程,即进入就绪状态。此时线程将进入线程队列排队
等待CPU资源,这表明它已经具备了运行的条件,在未获得CPU资源时,仍不能真正执行
- 运行状态
当就绪状态的线程获得CPU资源时,即可转入运行状态,执行run()方法。对只有一个cpu的计算机而言
,任何时刻只能有一个处于运行状态的线程占用cpu,即获得cpu资源
- 阻塞状态
一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态,而处于这种状态的线程在得到一个特定的事件之后转会可运行状态
可能使线程成为阻塞状态的条件是:
- 由于线程的优先级比较低,因此它不能获得cpu资源
- 使用sleep()方法使线程休眠
- 通过调用wait()方法,使线程等待
- 通过调用yield()方法,使线程让出cpu控制权
- 线程由于等待一个文件,i/o事件被阻塞
(5)死亡状态
一个线程的run()方法运行完毕后,线程则进入死亡状态,处于死亡状态的线程不具有继续运行的能力
- 线程的调度
按照特定机制为多个线程分配cpu的使用权
- 线程的优先级
每个线程会自动获得一个线程优先级(5),优先级的高低反应线程的重要或紧急程度。那么此刻,一般情况下优先级高的线程获得CPU资源的概率较大,但这个结果不是绝对的线程的优先级用1-10来表示,1.表示优先级最低,10表示优先级最高,默认值是5,这些优先级
对应一个Thread类的公用静态常量,例如
- public static final int NORM_PRIORTY=5;
- public static final int MAX_PRIORTY=10;
- public static final int MIN_PRIORTY=0;
每个线程的优先级介于Thread.MIN_PRIORTY和Thread.MAX_PRIORTY之间,线程的优先级可以通过
getPriority()方法获取,setPriority(int grade)方法来更改,后者的参数表示要设置的优先级,它必须是 1-10的整数。
例如:
线程对象.setPriority(MAX_PRIORTY);
线程对象.setPriority(4);
- 线程的休眠
在程序中允许一个线程进行暂时休眠,直接使用Thread.sleep()方法即可实现线程的休眠。
Public satic void sleep(long millis)
Thread.sleep();
sleep()会让当前线程休眠(停止执行)millis毫秒,线程由运行中的状态进入不可运行的状态。
睡眠时间过后线程会再次进入可运行状态。调用sleep()方法需处理InterruptedException异常
- 线程的强制运行
Join()方法使当前线程暂停执行,等待调用该方法的线程执行结束后在继续执行本线程。它有
三个重载方法。
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
与sleep()方法一样,调用join()方法需要处理nterruptedException异常。
- 线程的礼让
yield()方法可暂停当前线程执行,允许其他具有相同优先级的线程获得运行机会,该线程仍处于就绪状态,不转为阻塞状态,此时系统选择其他相同或更高优先级线程执行,若无其他相同或更高优先级线程,则该线程继续执行
public static void yield()
Thread.yield();
使用yield()的线程礼让只是提供一种可能,但是不能保证一定会实现礼让,因为礼让的线程处于就绪状态,还有可能被线程处于就绪状态,还有可能被线程调度程序再次选中
- 线程同步
当两个或多个线程需要访问统一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用
这就称为线程同步
- 同步方法:
访问修饰符 synchronized 返回类型 方法名(参数列表){//代码}
synchronized 访问修饰符 返回类型 方法名(参数列表){//代码}
缺陷:如果将一个运行时间比较长的方法声明成 synchronized 将会影响效率
- 同步代码块
Synchronized(syncObject){//代码}
在同步代码块时必须指定一个需要同步的对象,但一般都将当前对象(this)设置成同步对象
注意:
使用线程同步时
- 当多个并发线程访问同一个对象object的Synchronized(this)同步代码块时,同一时刻只能有一个线程得到执行,其他线程必须等待当前线程执行完毕之后才能执行该代码块
- 当一个线程访问object的一个Synchronized(this)同步代码块时,其他线程对object中所有其他
Synchronized(this)同步代码块的访问将被阻塞。即该线程获得这个object的对象锁,其他线程对该object对象所有同步代码部分的访问都将被阻塞
(3)当一个线程访问的object的一个synchronized(this)同步代码块时,其他线程仍可以访问该object的非
synchronized(this)同步代码块
- 线程安全的类型
所在的进程中有多个线程,而当这些线程同时运行时,每次结果和单线程运行的结果是一样的,而其他变量的值和预期是一样的,就是线程安全的