1.线程的五种状态
- 新建状态 new 线程创建。 Thread t = new MyThread();
- 就绪状态 runnable 当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
- 运行状态 running 当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
- 阻塞状态 blocked
处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态 dead 线程执行完/异常,结束生命周期。
PS:运行状态的转变,
就绪状态转换为运行状态:当此线程得到处理器资源;
运行状态转换为就绪状态:当此线程主动调用yield()方法或在运行过程中失去处理器资源。
运行状态转换为死亡状态:当此线程线程执行体执行完毕或发生了异常。
此处需要特别注意的是:当调用线程的yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。
转自:https://blog.youkuaiyun.com/xiaosheng900523/article/details/82964768
2. Thread 类中的start() 和 run() 方法有什么区别?
start()方法被用来启动新创建的线程,而且start()内部 调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启 动,start()方法才会启动新线程。
3.java开启新线程的三种方法
转自: https://www.cnblogs.com/lgjava/p/9997126.html
方式1:继承Thread类,重写run然后使用start
步骤:
1):定义一个类A继承于Java.lang.Thread类.
2):在A类中覆盖Thread类中的run方法.
3):我们在run方法中编写需要执行的操作:run方法里的代码,线程执行体.
4):在main方法(线程)中,创建线程对象,并启动线程.
(1)创建线程类对象:
A类 a = new A类();
(2)调用线程对象的start方法:
a.start();//启动一个线程
注意:千万不要调用run方法,如果调用run方法好比是对象调用方法,依然还是只有一个线程,并没有开启新的线程.
线程只能启动一次!
创建启动线程实例:
//1):定义一个类A继承于java.lang.Thread类. class MusicThread extends Thread{ //2):在A类中覆盖Thread类中的run方法. public void run() { //3):在run方法中编写需要执行的操作 for(int i = 0; i < 50; i ++){ System.out.println("播放音乐"+i); } } } public class ExtendsThreadDemo { public static void main(String[] args) { for(int j = 0; j < 50; j ++){ System.out.println("运行游戏"+j); if(j == 10){ //4):在main方法(线程)中,创建线程对象,并启动线程. MusicThread music = new MusicThread(); music.start(); } } } }
方式2:实现Runnable接口,重写run方法,然后使用 Thread t = new Thread(mi); t.start();
步骤:
1):定义一个类A实现于java.lang.Runnable接口,注意A类不是线程类.
2):在A类中覆盖Runnable接口中的run方法.
3):我们在run方法中编写需要执行的操作:run方法里的,线程执行体.
4):在main方法(线程)中,创建线程对象,并启动线程.
(1)创建线程类对象:
Thread t = new Thread(new A());
(2)调用线程对象的start方法:
t.start();
//1):定义一个类A实现于java.lang.Runnable接口,注意A类不是线程类. class MusicImplements implements Runnable{ //2):在A类中覆盖Runnable接口中的run方法. public void run() { //3):在run方法中编写需要执行的操作 for(int i = 0; i < 50; i ++){ System.out.println("播放音乐"+i); } } } public class ImplementsRunnableDemo { public static void main(String[] args) { for(int j = 0; j < 50; j ++){ System.out.println("运行游戏"+j); if(j == 10){ //4):在main方法(线程)中,创建线程对象,并启动线程 MusicImplements mi = new MusicImplements(); Thread t = new Thread(mi); t.start(); } } } }
分析继承方式和实现方式的区别:
继承方式:
1):从设计上分析,Java中类是单继承的,如果继承了Thread了,该类就不能再有其他的直接父类了.
2):从操作上分析,继承方式更简单,获取线程名字也简单.(操作上,更简单)
3):从多线程共享同一个资源上分析,继承方式不能做到.
实现方式:
1):从设计上分析,Java中类可以多实现接口,此时该类还可以继承其他类,并且还可以实现其他接口,设计更为合理.
2):从操作上分析,实现方式稍微复杂点,获取线程名字也比较复杂,得使用Thread.currentThread()来获取当前线程的引用.
3):从多线程共享同一个资源上分析,实现方式可以做到(是否共享同一个资源).
补充:实现方式获取线程名字:
String name = Thread.currentThread().getName();
方式3:直接在函数体使用(匿名内部类,其实也是属于第二种实现方式的特例。重写Runnable中的run方法,然后使用start)
void java_thread() { Thread t = new Thread(new Runnable(){ public void run(){ // run方法具体重写 mSoundPoolMap.put(index, mSoundPool.load(filePath, index)); getThis().LoadMediaComplete(); }}); t.start(); }