通过集成Thread类来创建并启动多线程的步骤如下:
(1)定义Thread类的子类,并重写该类的run()方法,该方法代表了线程需要完成的任务。因此run()方法体称为线程执行体。
(2)创建Thread子类的实例,即创建线程对象。
(3)调用线程对象的start()方法,启动该线程。
Thread类的常用方法:
方法 | 说明 |
void setName(String name) | 设置线程的名字 |
String getName() | 返回当前线程的名字 |
Thread currentThread() | 返回当前正在执行的线程对象 |
void start() | 启动线程(对一个已经死亡的线程调用会抛出异常(报错) ) |
String getAbsolutePath() | 返回此File对象所对应的绝对路径名 |
void run() | 线程一旦执行,便调用此方法 |
boolean isLive() | 判断当前线程是否活跃,活跃返回true,否则返回false |
boolean isAlive() | 当线程处于就绪、运行和堵塞状态返回true,当线程处于新建和死亡返回false |
void sleep(long millis) | 让当前正在执行的线程暂停多少毫秒,并进入阻塞状态 |
void join() | 当前线程等待调用该方法的线程死亡后执行。 |
void join(long millis) | 当前线程等待调用该方法的线程死亡后执行。最多等待多少秒。 |
在java中,除了继承Thread类,还有另外一种方式就是实现Runnable接口。
实现Runnable接口,创建并启动多线程的步骤如下:
(1)定义Runnable接口的实例类,并重写该接口的run()方法,改run()方法的方法体同样是该线程的线程执行体。
(2)创建Runnable实例类的实例,并以该实例作为Thread实例操作的目标。
(3)创建Thread类对象,Thread类对象为实际的线程对象。
区别在于继承是单一的,但接口可以有多个。这样的好处是使得程序具有更好的可扩展性。
当线程被创建后,要经过:
新建 -- 就绪 -- 运行 -- 阻塞 -- 死亡
尤其是线程启动后,他不可能独占CPU资源供自己运行,所以CPU需要在多条线程之间切换,于是线程也会多次在运行和阻塞之间切换。
新建状态:
当程序使用new关键字创建线程后
就绪状态:
当线程对象调用了start()方法后
运行状态:
当开始执行run()方法的线程执行体后
阻塞状态:
(1)CPU执行其他线程时
(2)线程调用sleep()方法主动放弃占有的CPU资源
(3)线程调用了一个堵塞式的IO方法,在方法之间,该线程被堵塞
死亡状态:
(1)run()方法执行完成,线程正常结束
(2)线程抛出一个未捕获的Exception或Error
(3)直接调用线程的stop()方法来结束该线程(通常不推荐使用该方法)
同步代码块:
synchronized(要锁定的对象){
//同步代码块执行体
}
public synchronized void run() {
//同步代码块执行体
}
同步代码块要写在run方法内部或者作为修饰符对方法进行修饰
修饰方法的效率略高于内部代码块
代码执行体只能被一条线程所访问,其他线程必须等当前线程释放了该锁后才可以执行
当前线程不管执行完毕还是抛出异常,都会释放锁。其他线程可继续执行
死锁:
死锁是指多个线程因竞争资源而造成的一种僵局(互相等待)
飞连接----java实例 死锁及解决方法 | 菜鸟教程
ThreadLocal:
理解不深刻,飞连接----ThreadLocal-简书