初识线程(原创)
一,线程与进程
先来解释一下线程和进程的东西
1,线程:进程中负责程序执行的执行单元 线程本身依靠程序进行运行 线程是程序中的顺序控制流,只能使用分配给程序的资源和环境 2,进程:执行中的程序,一个进程至少包含一个线程。 3,单线程:程序中只存在一个线程,实际上主方法就是一个主线程 4,多线程:在一个程序中运行多个任务,目的是更好地使用CPU的资源二,线程的实现:
线程的实现有两种,一种是继承Thread类 一种是实现Runable接口如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法 是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动 新线程并执行自己定义的run()方法。
三,线程的状态: 创建(new)状态:准备好了一个多线程的对象 就绪(runnable)状态:调用了start()方法,等待CPU进行调度 运行(running)状态:执行run()方法。 阻塞(blocked)状态:暂停停止执行,可能将线程交给其他线程使用 终止(dead)状态:线程销毁 当需要新起一个线程来执行某个子任务时,就创建了一个线程,但是线程创建之后,不会立即进入就绪状态。 因为线程需要创建一些条件,只有线程运行需要的所有条件都满足了,才进入就绪状态。当线程进入了就绪状态后,不代表立即就能获取CPU执行时间,也许此时CPU正在执行其他事情,
因此需要等待,当CPU执行时间之后,线程便会真正进入了运行状态线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠
睡眠一定时间后再执行,用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting (睡眠或等待一定的时间),waiting(等待被唤醒),blocked(阻塞); 当由于突然中断或者子任务执行完毕,线程就会自动消亡 在有些教程上将blocked,waiting,time waiting统称为阻塞状态,这个也是可以的,只不过这里我想将 线程的状态和JAVA中的方法调用联系起来,所以将waiting和 time waiting两个状态分离出来。sleep(睡眠)和wait(等待被唤醒)的区别:
sleep是Thraed类的方法,wait是Object类中定义的方法。 Thread.sleep不会导致锁行为的改变,如果当前线程是拥有锁的,那么Threed.sleep不会让线程释放锁 Thread.sleep和Object.wait都会暂停当前的线程。OS会将执行时间分配给其他线程,区别是,调用wait后 需要别的线程执行motify/notifyAll才能够重新获得CPU执行时间 上下文切换 对于单核CPU来说,CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个程序,这个叫做线程上下文切换 由于可能当前线程的任务并没有执行完毕,所以在切换是需要保存线程的运行状态,以便下次重新切换回来时能够来回切换之前的状态运行。举个简单的例子:比如一个线程A正在读一个文件内的内容,正读到文件的有Ibanez,此时需要暂停A去执行B,当再次切换线程A的时候,
我们不希望线程A又从文件的开头来读取
因此需要记录线程A的运行状态,那么会记录那些数据呢? 因为下次恢复时要知道在这之前当前线程已经执行到那条指令了。所以需要记录程序计数器的值,另外比如说线程正在进行 没结束计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起的变量的值是多少,因此需要记录CPU寄存器的状态。 所以一般来说,线程上下文切换过程中会记录表程序计数器,CPU寄存器状态等数据 简单来说:对于线程的上下文切换实际上就是 存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以
在进行编程时要注意哪些因素四,线程的常用方法
public void start() 使线程开始执行,
JAVA虚拟机调用该线程的run()方法
public void run() 如果该线程是使用独立的Runable运行该对象构造的,则调用该Runnable对象的run()方法。否则该方法不执行任何从操作并返回。
public final void setName(String name) 改变线程名称,使之与参数name相同
public final void setPriority(int priority) 更改线程的优先级
public final void setDeamon(boolwan on)将该线程标记为守护线程和用户线程
public fianl void join(long millisec)等待该线程终止的时间最长为millis毫秒
public void interrupt() 中断线程
public final boolean isAlive()测试线程是否处于活动状态
public static void yield() 暂停当前正在执行的线程的线程对象,并执行其他线程。
public static void sleep(long millisec)在指定的毫秒数内让当前正在执行的线程休眠(暂停运行 此操作受到系统计时器和调度程序精度和精确度的影响。)
public static Thread currentThread() 返回对当前旨在执行的线程对象的引用
线程的创建: Thread() Thread(String name) Thread(Runnable target) Thread(Runnable target,String name)
五,线程的方法
void start() 启用线程
static void sleep(long millis) 线程休眠
static void sleep(long millis,int nanos)线程休眠
void join() 使得其他线程,等待当前线程终止
void join(long millis)
void join(long millis,int nanos) 当前运行线程释放处理器资源
static void yield 暂停当前正在执行的线程的对象,并执行其他线程 获得线程引用 static Thread currentThread() 返回当前运行的线程引用静态方法
currentThread()方法可以返回代码段正在被那个线程调用的信息
sleep()方法 作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程
sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务,但都是有一点要非常注意,sleep方法不会释放锁 也就是说如果当前线程持有某个对 象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。 yield()方法 调用yieid方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是 yield不能控制具体的交出CPU 的时间,另外,yieId方法只能让拥有相同优先级的线程 有获取CPU执行时间的机会 注意:调用yieId方法并不会让线程进入阻塞状态,而是让线程重回到就绪状态,它之需要等待重新获取CPU的执行时间 start()方法 用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源 run()方法 run()方法是不需要用户调用的,当调用start方法启动一个线程之后,当线程获得了CPU执行时间,便进入了run方法 体去执行具体的任务,注意,继 承Thread类必须重写run方法,在run方法中定义具体要举行的任务 getId() getId()的作用是取得线程的唯一表示 Thread t=new Thread.currentThread(); t.getId(); isAlive()方法 方法isAlive()的功能是判断 当前线程是否处于活动状态 Thread t=new Thread(); t.isAlive(); 什么是活动状态呢? 活动状态就是线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的, Jion()方法: 在很多情况下,主线程创建并启动了线程,如果子线程要进行大量耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待 子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了,方法join()的作用是等待线程对象 销毁。 getName和setName:用来得到或者设置线程名称 getPriority和setPriority:用来获取和设置线程优先级 setDaemon和isDaemon: 用来设置线程是否成为守护线程和判断线程是否是守护线程 守护线程和用户线程的区别在于:守护线程依赖于创建他的线程,而用户线程则不依赖。 举个例子:如果在main线程中创建 了一个守护线程,当main方法运行完毕后,守护线程也会随之消亡。而用户线程则不会。而用户线程会一直运行知道其运行 完毕。在jvm中,像垃圾收集器线程就是守护线程。 停止线程 停止线程是在多线程开发时很重要的技术点,掌握此技术可以对该线程的停止进行有效的处理 在JAVA中有以下3种方法可以终止正在运行的线程: 1,使用退出标志,使线程正常退出,也就是当run方法完成后线程终止 2,使用interrupt方法中断线程,但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止 3,暂停线程 interrupt()方法六,线程的优先级 在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象的任务 设置线程优先级使用setPriority()方法 在JAVA中,线程的优先级分为1-10个等级,如果小于1或者大于10;JDK会抛出异常 illgalumentException(). 线程的优先级特性, 比如A线程启动B线程,则B线程的优先级和A是一样的 规则性 高优先级的线程总是大部分先执行完,但不代表优先级线程全部先执行完 随机性 优先级较高的线程不一定每一次都先执行完七,守护线程 在JAVA线程中有两种线程,一种是User Thread 用户线程 ,另一种是Deamon Thread 守护线程 守护线程并非虚拟机内部可以提供,用户也可以自行的设定守护线程,方法:public final coid setDaemon(boolean on),但是有几点要定义thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出异常
在Daemon线程中产生的新线程也是Deamon的不是所有的应用都可以分配给Deamon线程来进行服务,比如读写操作 或者计算逻辑。八,同步代码块: 在代码块上加上"synchronized"关键字,则此代码块就称为同步代码块. 同步代码块的格式: synchronized(同步对象){ 需要同步的代码块 } 同步方法 synchronized void 方法名称(){ }Runnable方式可以避免Thread方式由于JAVA单继承特性带来的缺陷