Java多线程小总结


1.线程的创建

run()方法,call()方法都是线程执行体。不要直接调用哟。

创建线程的方法:

  • 继承Thread类(这个类也是实现了Runnable接口,你记得吗?)
  • 实现Runnable接口(Runnable接口只有一个run()方法,属于函数式接口,可以使用lambda表达式,你知道吗?)(最常用的方法)
  • 实现Callable接口和Feture接口 (用过吗?比起Runnable接口,它可以有返回值,也可以声明异常,人称增强版Runnable接口)

继承Thread类的线程创建步骤:

  1. 继承Thread类,重写run()方法
  2. 创建子类实例对象
  3. 调用子类实例的start()方法启动线程
public class TestThread  {
    public static void main(String[] args) {
        Thread thread = new Thread(){  //内部类可以这样写哦,对比一下下面的lambda表达式方法
            @Override
            public void run() {
                for(;;){
                    try {
                      Thread.sleep(1000);  //每秒打印一次
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("I am thread");
                }
            }
        };                  //继承Thread类,重写run()方法
        thread.start();     //启动线程
    }
}

实现Runnable接口的线程创建步骤:

  1. 创建Runnable接口的实现类,重写run()方法
  2. 创建实现类的实例对象,并作为target参数传递到Thread类的构造方法中创建Thread对象。
  3. 调用新的Thread实例对象的start()方法启动线程

对于这个Runnable接口,优先使用lambda表达式

public class TestRunnable{
    public static void main(String[] args) throws Exception{
        Thread thread = new Thread(  //本来就是要将Runnable接口实现类的实例传入的,只是使用了lambda表达式
        ()->{                        //年轻人,如果你不用lambda表达式,你需要多几行无用代码
            for(;;){                 //如果你再写一个实现类,那就显得有些多余
                try {
                    Thread.sleep(1000);         //一秒打印一次
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                }
                System.out.println("I am Runnable's son");
            }
        },"thread1");               //这里添加的名字,只是一个标识
        thread.start();             //启动线程
    }
}

使用Callable接口和Future接口创建线程的步骤:

  1. 创建Callable接口的实现类,重写call()方法(可以有返回值);
  2. 将Callable接口实现作为构造器参数,创建FutureTsak实例;
  3. 将FutureTask实例作为构造器参数,传入Thread创建新线程;
  4. 可以通过FutureTask实例的get()方法获得返回值

特别指出:

  • Callable接口(带有泛型)也是函数式接口,里面只有一个call()方法,泛型的作用指定返回值类型;
  • 虽然创建线程时,使用的是FutureTask类型的实例,这只是一种包装,实际上仍是运行call方法;
  • FutureTask类是Future接口的实现类,不要说怎么没用到Future接口噢,Future接口的存在就是为了获得返回值;(这个继承过程建议看源码,这里只是简单指出)
  • Future接口中的公共方法(用于控制关联的Callable任务),比较重要的就是两个get()方法,因为这个方法运行是会导致线程阻塞,因此一个get()方法用于完成获取返回值,会一直阻塞线程,另一个get()方法则有最大阻塞时间限制,由自己设定。具体参考源码。
public class TestCallable {
    public static void main(String[] args) throws Exception{
        FutureTask futureTask = new FutureTask((Callable) ()->{
            return 1;
        });      //创建FutureTask类型实例,参数是Callable接口实现类实例,泛型表示返回值类型
        Thread thread = new Thread(futureTask);      //传入FutureTask类型实例创建线程
        thread.start();                              //启动线程
        System.out.println(futureTask.get());        //获得线程返回值 1
    }
}

2.线程的生命周期

Java多线程小总结_第1张图片


3.线程优先级

优先级的存在已无需多言,等级观念,看来是无法避免的。

优先级取值范围是 [1,10],默认优先级为5;优先级值越大,优先级别越高。

两个方法设置/获取线程优先级:

方法名 描述
public final void setPriority(int newPriority) 设置线程优先级值
public final int getPriority() 获取对应的线程优先级值

优先级静态常量:

常量名
MAX_PRIORITY 10
NORM_PRIORITY 5
MIN_PRIORITY 1

线程的常用控制方法

join()方法:我是大哥,谁不服?

完整方法名:public final void join()
  这个方法很霸道,他在哪,别人都要让着他。调用这个方法的线程,别的线程会先让他执行(看参数决定执行时长)。果然是大哥。


yield()方法:别说我不给你们机会

完整方法名:public static native void yield()
  这是一个有个性的方法。调用这个方法的线程非常自大,会自己放弃CPU的占用,重新回到就绪状态,然后再和其他线程竞争CPU的使用权。
  “你们不是说我一直霸占着CPU吗?别说我不给你们机会,来,让线程调度器再选择一次,我们公平竞争。”
  但是,基本上,只有与这个线程优先级相同或者优先级更高的,并且处于就绪状态的其他线程才有执行机会


sleep()方法:困了,累了,让朕先歇一歇

完整方法名: public static native void sleep(long millis)
  调用这个方法的线程,可以说是与世无争,也可以说是自甘堕落,他会睡觉!
  调用这个方法之后,线程会由执行状态转换为阻塞状态。注意一点,只有轮到这个线程执行的时候,它才有权选择睡觉。
  不过,虽然是很懒,但是也会听我们的安排,它睡多久,也完全由我们调的闹钟去控制。


isAlive()方法:兄弟,你还活着吗?

完整方法名: public final native boolean isAlive()
  就绪,运行,阻塞 -> true;
  新建,死亡 -> false;

待续吧。。。

你可能感兴趣的:(多线程)