线程的创建与相关操作

版权声明:ssupdding https://blog.csdn.net/sspudding/article/details/88945327

线程的创建方式

一、实现runnable接口

1、步骤:

  • 创建自定义类并实现runnable接口,并实现接口中的run方法
  • 实例化自定义的类
  • 将自定义类的实例作为参数给Thread类,创建thread实例
  • 调用thread实例的start方法,启动子线程

2、使用示例

public class RunnableTest implements Runnable {
    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName()+" subthread start");
        System.out.println(Thread.currentThread().getName()+" subthread end");
    }

}
public class Test {

    public static void main(String[] args) {

        System.out.println(Thread.currentThread().getName()+" main thread start");
        RunnableTest runnableTest = new RunnableTest();
        Thread thread = new Thread(runnableTest);
        thread.start();
        System.out.println(Thread.currentThread().getName()+" main thread end");
    }
}

二、继承Thread类

1、步骤

  • 创建自定义的类继承Thread类,并且重写run接口
  • 实例化自定义的类
  • 通过实例化对象调用start方法来创建新线程

2、使用示例

public class ThreadTest extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" subthread start");
        System.out.println(Thread.currentThread().getName()+" subthread end");
    }
}
public class Test {

    public static void main(String[] args) {

        ThreadTest threadTest = new ThreadTest();
        System.out.println(Thread.currentThread().getName()+" main thread start");
        threadTest.start();
        System.out.println(Thread.currentThread().getName()+" main thread end");
    }
}

三、实现Callable接口

callable接口的实现是线程池提供的一种创建线程的方式

1、步骤

  • 实现Callable接口,并且实现call方法
  • 创建线程池(Executors工具类提供的方法创建线程池)
  • 创建Callable接口实现类的实例
  • 将实例对象通过线程池的submit方法提交给线程池进而创建新的线程

2、使用示例

public class CallableTest implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" subthread start");
        Integer i =100;
        System.out.println(Thread.currentThread().getName()+" subthread end");
        return i;
    }
}
public class Test {

    public static void main(String[] args) {

        System.out.println(Thread.currentThread().getName()+" main thread start");
       
        ExecutorService  executor = Executors.newSingleThreadExecutor(); //创建线程池
        CallableTest callableTest = new CallableTest();
        executor.submit(callableTest);
     
        System.out.println(Thread.currentThread().getName()+" main thread end");
    }
}

tips:线程之间的执行是相互独立的,哪一个线程优先执行取决于OS的调度

获取和设置线程对象名称

  • 获取线程对象的名称

        public final String getName():获取线程的名称。

  • 设置线程对象的名称

            public final void setName(String name):设置线程的名称

            用有参构造设置线程名称  eg:MyThread thread = new MyThread("线程1");

  • 针对不是Thread类的子类获取线程对象名称

      Thread.currentThread().getName()  ( public static Thread currentThread():返回当前正在执行的线程对象

线程调度

线程有两种调度模型:

  • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
  • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

而Java使用的是抢占式调度模型。

获取线程对象的优先级: public final int getPriority():返回线程对象的优先级

设置线程对象的优先级: public final void setPriority(int newPriority):更改线程的优先级。 

MIN_PRIORITY = 1; 线程的最小优先级
NORM_PRIORITY = 5; 线程的默认优先级
MAX_PRIORITY = 10; 线程的最大优先级

如果设置的优先级参数超过优先级的范围,会抛出IllegalArgumentException非法参数异常。

优先级分为10级,优先级数字越大,即优先级越高。线程优先级高仅仅表示线程获取的 CPU时间片的几率高,被优先调用的概率会大,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。

 

线程操作的相关方法

  • void run():

run()方法是子线程的执行体,子线程从进入run方法开始,直至run方法执行结束,意味着子线程的任务执行结束。

在主线程直接调用run方法不能创建子线程,其实就相当于普通方法的调用,所以是单线程的效果

  • void start():启动一个新的线程。

start()方法必须是子线程第一个调用的方法,start不能够重复调用,新线程会调用runnable接口提供的run方法

start()方法就呈现的是多线程的效果

run()和start()的区别是什么呢??

run():仅仅是封装被线程执行的代码,直接调用是普通方法

start():首先启动了线程,然后再由JVM去调用该线程的run()方法

  • static void sleep(long millis):线程休眠

示例:

public class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x + ",日期:" + new Date());
            // 睡眠
            try {
                Thread.sleep(1000); //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



public class Demo {

    public static void main(String[] args) {
        ThreadSleep ts1 = new ThreadSleep();
        ThreadSleep ts2 = new ThreadSleep();

        ts1.setName("One");
        ts2.setName("Two");

        ts1.start();
        ts2.start();
    }
}

部分结果展示:

  • final void join():等待该线程终止

等待线程执行结束才继续执行,会抛出 InterruptedException异常。

假如在a线程中b线程进行b.join调用,a线程等待b线程执行结束后才能继续执行,控制多线程按照次序执行。

(要在start()方法执行之后执行)

  • static void yield():暂停当前正在执行的线程对象,并执行其他线程。

这是Thread类的静态方法。让正在执行的线程停止或者让步,让给优先级较高的线程获取CPU的执行权,(不一定会让出CPU执行权,如等待的线程优先级较低或者当前只有一个线程在执行时,那么当先线程又会立即获取CPU的执行权)

  • final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。

true表示设置为守护线程

false表示用户线程 ,默认是用户线程

当正在运行的线程都是守护线程时,Java 虚拟机退出。 所以该setDaemon()方法必须在启动线程前调用。 

用户线程和守护线程区别:

守护线程的生命周期是依赖于用户线程,当有用户线程存在,守护线程就会存活,当没有用户线程存在,那守护线程也随之消亡,垃圾回收是有单独线程来处理的,负责垃圾回收的线程就是守护线程

  • boolean isDaemon():测试该线程是否为守护线程

守护线程:脱离于空中终端,作为提供通用服务的线程存在

  • void interrupt():中断线程

把线程的状态终止,并抛出InterruptedException异常

该方法底层调用的是native方法。native方法的作用是对特定标识位做修改

该方法主要作用于线程:运行中线程、阻塞线程(sleep、join)
运行中的线程:interrupt方法仅仅是对标志位做了修改,其他没有影响
阻塞线程:interrupt方法对标志位做了修改,另阻塞中的线程感知到标志位做了修改,就会中断当前的阻塞状态,抛出InterruptedException异常 

  • boolean isInterrupted() :测试线程是否已经中断

true:表示发生中断

false:表示未发生中断

线程的生命周期

使用练习

有线程A、B、C,让线程按C、B、A顺序执行

public class TestThread extends Thread{
     private Thread thread;

     public TestThread(){
         this.thread = null;
     }

     public TestThread(Thread thread){
         this.thread = thread;
     }
    @Override
    public void run() {
        if (thread != null){
            try {
                this.thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println(Thread.currentThread().getName()+"running ");
    }
}
public class Test1 {

    public static void main(String[] args) {

        TestThread TC = new TestThread();
        TC.setName("C");
        TestThread TB = new TestThread(TC);
        TB.setName("B");
        TestThread TA = new TestThread(TB);
        TA.setName("A");
        TA.start();
        TB.start();
        TC.start();

    }
}

运行结果:

猜你喜欢

转载自blog.csdn.net/sspudding/article/details/88945327