线程:
线程是并发执行代码的。
* 有两种创建方式:
* 方式一:继承Thread并重写run方法。run方法中就是希望线程执行的逻辑
* 方式二:实现Runnable接口。单独定义线程接口
方式一创建线程的方法比较简单直接,但是缺点主要有两个:
1、由于需要继承线程,这导致不能在继承其他类,在实际开发中经常要复用某个超类的功能,那么在继承线程后不能再继承其他类会有很多不便;
2、定义线程类的同时重写了run方法,这会导致线程与线程要执行的任务有一个必然的耦合关系,不利于线程的重用。
线程图如下:
线程提供一个静态方法:static Thread currenThread();// 该方法用来获取运行这个方法的线程
线程提供获取自身信息的方法:
getName()---------获取线程名字
getId()-----------获取线程ID
getPriority()-----获取线程优先级
isAlive()---------判断线程是否活着
isInterrupted()---判断线程是否被中断
isDaemon()--------判断是否为守护线程
线程提供一个方法:void join()
该方法可以协调线程之间的同步运行
线程提供一个静态方法:
static void sleep(long ms)
使运行这个方法的线程阻塞指定毫秒。超时后该线程会自动返回到Runnable状态,等待再次并发运行
注:sleep方法要求必须处理中断异常,原因在于当一个线程调用了sleep方法处于阻塞状态的过程中若被调用了它的interrupt()方法中断时,它就会在sleep方法中抛出中断异常。这时并非是将这个线程直接中断,而是中断了它的阻塞状态
线程的优先级
线程不能主动获取CPU时间片,只能被动的被线程调度器分配
调整线程的优先级可以最大程度的改善某个线程获取CPU时间片的次数
理论上线程优先级越高的线程获取的CPU时间片的次数就越多
同步与异步:
同步运行:运行有顺序
异步运行:运行代码无顺序,多线程并发运行就是异步运行
多线程并发的安全问题
产生:当多个线程并发操作同一资源时,由于线程切换实际的不确定性,会导致执行操作资源的代码顺序未按照设计顺序执行,出现操作混乱的情况。严重时可能会导致系统的瘫痪。
解决:将并发操作同一资源改为同步操作,及:有先后顺序的操作
当一个方法被synchronized修饰后,该方法称为“同步方法”,即:多个线程不能同时在方法内部运行。强制让多个线程在执行同一个方法时变为同步操作就解决了并发安全问题。
在方法上使用synchronized,那么同步监视器对象就是当前方法所属对象(监视的动作属于的类),即:方法内部看到的this。
同步块
语法:
synchronized(同步监视对象){
需要同步的代码片段
}
同步块可以更精确的控制需要同步运行的代码片段。有效的缩小同步范围可以保证并发安全的前提下提高代码并发运行的效率。
使用同步块控制多线程同步运行必须要求这些线程看到的同步监视对象为同一个。
静态方法若使用synchroinzed修饰,那么该方法一定具有同步效果。静态方法对应的同步监视器对象为当前类的类对象(class的实例)。
互斥锁:
* 当多个代码片段被synchronized块修饰后,这些同步块的同步监听器对象又是同一个时,这些代码片段是互斥的,多个线程不能同时在这些方法运行。
守护线程
* 守护线程又称之为后台线程,默认创建的线程都是普通线程或者称为前台线程,线程提供一个方法:
* void setDaemon(boolean on)
* 只有调用该方法并传入参数true时,该线程才会被设置为守护线程
*
*
* 守护线程在使用上与普通线程没有差别,但是在结束时机上有一个区别,即:
* 线程结束时所有正在运行的守护线程都会被强制停止
*
* 线程的结束:当一个进程中所有的普通线程都结束时,进程即结束
线程池:
1、线程池可以理解为公交车总站:池子就是整个公交车总站,线程就是里面的公交车。
2、线程池的作用:
1:控制线程数量
2:重用线程
创建线程池的步骤:
//创建一个线程池,线程数量为50
1、ExecutorService threadpool=Executors.newFixedThreadPool(50);
2、//将任务指派给线程池
threadpool.execute(runn);
停止线程池的两个方法:
shutdown()方法调用后,线程池不再接收新任务,并且会将线程所有的任务执行完后再停止
shutdownNow()方法调用后,立即停止