线程创建与常用方法介绍

线程创建与常用方法介绍

一、线程创建方式

- 实现Runable接口
- 继承Thread
- 实现Callable接口

实现Runable接口

public interface Runnable {
    //抽象的run方法
    public abstract void run();
}

实现Runnable接口,实现其提供的run方法

Runnable实现类使用Demo

//Runnable实现类的使用
RunableDemo runable = new RunableDemo();
Thread thread = new Thread(runable, "A");
//子线程的启动需要调用start方法
thread.start();

使用步骤:
1、创建一个Runnable接口的实现类,并实现run()方法
2、实例化Runnable接口实现类
3、创建Thread类实例,将实例化的Runnable实例作为参数传递
4、启动子线程,调用Thread类的实例的start方法

继承Thread

Thread类的定义:

public class Thread implements Runnable

Thread类也是Runable接口的实现类,实现类run方法

public void run() {
        if (target != null) {
            target.run();
        }
    }

继承Thread类时,重写run方法

继承Thread类使用Demo

ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();

使用步骤:
1、创建类,继承Thread(extends Thread),重写run方法
2、实例化当前创建的Thread类的子类
3、启动子线程,调用start方法

实现Callable接口

Callable接口的什么如下:

public interface Callable<V> {
    V call() throws Exception;
}

Callable接口提供了call方法,具有范围值,通过泛型来定义,该接口可以抛出异常

FutureTask类声明:

public class FutureTask<V> implements RunnableFuture<V>

//RunnableFuture接口声明
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

FutureTask类是继承Runable接口,即FutureTask是Runable接口的实现类,而FutureTask类提供了构造函数FutureTask(Callable callable),可以接受Callable类型的任务

Callable接口的使用Demo:

//实现Callable接口
        CallableDemo callableDemo = new CallableDemo();
        //通过FutureTask可以将Callable实现类兼容成Runable实现类
        FutureTask futureTask = new FutureTask <>(callableDemo);
        Thread thread = new Thread(futureTask);
        thread.start();

使用步骤:
1、创建类,实现Callable接口,实现其提供的call方法
2、创建Callable实例
3、创建FutureTask实例,将Callable实例作为参数传入
4、创建Thread类实例,将FutureTask实例作为参数传入(当做Runable实例)
5、启动子线程,调用Thread类的start方法

Runable接口和callable接口区别:
1、Callable接口规定的方法是call()方法,Runnable接口规定的方式是run()方法
2、Callable的任务执行后有返回值,而Runable的任务不能有返回值
3、call()方法可抛出异常,run()方法不能抛出异常
4、通过运行Callable的实例可以获取到FutureTask对象,FutureTask对象表示异步执行的结果,他提供了get方法可以异步获取线程执行的结果(get方法会阻塞当前线程),可以了解子线程的执行情况,可以取消任务的执行

线程状态及状态转换

Java中线程状态由6种

public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

NEW:新建状态

用new创建的线程处于新建状态,此时他和其他的Java对象一样,仅仅在堆中分配了内存

Runable:就绪状态

当线程对象被创建后,调用了start方法,线程就处于就绪状态,处于就绪状态的线程(其他的条件都满足,可以等待获取CPU的使用权),等待获取CPU的使用权

Running:运行状态

处于运行状态的线程也就占用的CPU,执行线程代码。只能有就绪状态的线程进入到运行状态

Blocked:阻塞状态

阻塞状态是指线程因为某些原因放弃CPU(缺少资源:IO、锁),暂时停止运行,当前的线程处于阻塞状态

Waiting:等待状态

当线程处于该状态,如果某个线程中的对象的wait()时,JVM就会将线程放入到等待池池

Timed_waiting:超时等待

sleep(long time),jion(long time) 会使线程处于睡眠状态

Terminated:终止状态

当线程执行到run()方法结尾时,就进入到终止状态,该表示线程的生命周期结束

线程状态转换图

在这里插入图片描述
通过线程转换可知:一个线程的生命周期中需要的状态:New、Runable、Running、Terminated四个状态
线程在需要响应的资源时,进入到阻塞状态:阻塞状态包含Waiting,Blocked、Time_waiting状态

线程常用方法介绍

start():启动线程

start方法启动一个新线程,start方法首先调用才能创建子线程,不能重复使用

public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
          //省略非核心代码
        }
    }

private native void start0(); //调用native方法

启动方法是需要底层OS来启动子线程,start是间接启动
native方法说明:
JNI:Java native interface,java本地方法接口(JMM中存在本地方法栈),调度系统本身的提供的非Java代码(C/C++代码)

run():子线程的执行体

整个子线程执行的业务逻辑都在run方法中
单独调用run方法,会在当前的线程中执行run()操作,和普通方法调用是一样的,不会启动子线程,run方法可以重复调用

public void run() {
        if (target != null) {
            target.run();
        }
    }

Thread类中的run方法只是判断任务体Runable是否存在,未做其他业务,继承Thread是需要重写

start()和run()方法的区别?

  1. start()方法是用来启动子线程,start方法启动子线程后自动的来调用run方法
    run()方法是子线程的业务执行体,不能直接调用run方法(通过调用run方法是不能操作子线程的)
  2. start()不能重复调用,但是run()可以重复调用

yield():线程让步

是用来暂停当前线程的执行,并且让步于其他相同优先级或更高优先级的线程先执行
yield():方法在thread类中,是Thread类中静态方法

yield方法特点:
1、yield方法让步CPU的资源,让给谁由系统决定的,一般是让给相同优先级或者更高优先级的线程获得执行权,如果没有的话,会执行原来的线程
2、yield让步:会让当前线程由“运行状态”进入到“就绪状态”,等待CPU的调度
3、yield让步CPU资源后,线程不会释放锁

public static native void yield();

join():线程合并

暂停当前线程执行,等待子线程的执行,也叫做线程合并,join方式是将并行执行的线程合并成串行执行,
例:在线程ta中调用tb.join,会暂停ta的执行,先让tb执行完毕,ta才会执行
方法介绍:
t.join() 允许t线程在当前线程之前执行,待t线程执行结束当前线程在执行
t.join(long millis)(时间单位:毫秒)允许t线程在当前线程之前执行,且最长时间millis毫秒之后,当前线程才能执行
t.join(long millis, int nanos)与t.join(long)一样,只不过可以提供纳秒级的精度
方法特点:
1、join方法是thread类中的方法,会抛出InterruptedException中断异常
2、当前线程ta中tb.join,tb会执行,ta线程会进入WAITING或TIMED_WAITING状态
3、当前线程ta中tb.join,则ta线程会释放当前持有的锁,join方法实现是通过wait/notify线程通信方式来实现的,wait方法的使用会释放锁

//一直等待子线程
public final void join() throws InterruptedException {
        join(0);
}
//提供毫秒,纳秒级别的等待时间
public final synchronized void join(long millis, int nanos) throws InterruptedException {
        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }
         //纳秒的处理最终转换为毫秒处理
        join(millis);
    }
//提供毫秒级别的等待时间
public final synchronized void join(long millis)throws InterruptedException {
        if (millis == 0) {
            //millis=0,会一直判断子线程是否结束,否则会一直等待
            while (isAlive()) {
                //线程等待
                wait(0);
            }
        } else {
            //判断子线程是否结束且是否到达指定的毫秒数,否则会等待
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
            }
        }
    }

eg:假如存在A、B、C三个线程,让三个线程按照C、B、A的顺序执行

package joinTestDemo;
class Thread1 extends Thread{
    public void run(){
        System.out.println("Threada");
    }
}


class Thread2 extends Thread{
    public void run(){
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Threadb");
    }
}


class Thread3 extends Thread{
    public void run(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Threadc");
    }
}

public class AbcSort  {

    public static void main(String[] args) throws InterruptedException {
        Thread3 thread3=new Thread3();
        thread3.start();
        thread3.join();
        Thread2 thread2=new Thread2();
        thread2.start();
        thread2.join();
        Thread1 thread1=new Thread1();
        thread1.start();

    }


}

interrupt():中断线程

用来中断当前线程,终止处于“阻塞状态”的线程
方法介绍:
interrupt():该方法在Thread类中,是一个普通方法,由对象调用该方法
boolean isInterrupted() 判断是否发送了中断操作, true:发生中断操作 false:未发生中断操作
方法特点:
1、如果当前线程是可中断的阻塞状态(join、sleep、wait等方法会导致线程进入阻塞撞状态:WATING / TIMED_WAITING状态),在任意其他的线程中调用interruprt方法,那么会立即抛出抛出InterruptedException来停止的阻塞状态
2、如果当前线程是可运行状态。调用interruprt方法,线程还是会继续执行,直到发生了sleep、join、wait等方法的调用,才会在进入阻塞之后,随后立即抛出InterruptedException,跳出阻塞状态

public void interrupt() {
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag 注意这句
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

private native void interrupt0(); //JNI方法,中断操作由系统来提供中断的方式

sleep():线程休眠

会让线程休眠,而且那个线程调用,那个线程休眠 ,TimeUtil.sleep(long),Threrd.sleep、或当前的额线程t.sleep,其结果都是当前的线程休眠
方法介绍:
sleep(long millis)
sleep(long millis, int nanos)
都是提供休眠操作,时间单位粒度不同
sleep 是Thread类提供的方法,会抛出InterruptedException异常
方法特点:
1、sleep休眠期间,会让出CPU使用权,但线程任然持有锁
2、sleep休眠时间到了之后,不会立即执行,而是线程由“阻塞状态”进入到“就绪状态”

public static native void sleep(long millis) throws InterruptedException  //JNI 

public static void sleep(long millis, int nanos) throws InterruptedException {
        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

deamon:守护线程

方法介绍:
setDaemon(boolean on) 设置线程为守护线程 true:表示是守护线程 false:非守护线程 默认false
boolean isDaemon():判断当前线程是否是守护线程 true:守护线程
Java中的线程主要有两种:用户线程和守护线程
守护线程和用户线程是什么?
用户线程一般用户执行的用户级的线程
守护线程:也叫做后台线程,脱离于终端,用来服务于用户线程 例如:GC是一个单独的线程来处理,GC线程就是一个守护线程

守护线程的生命周期?
守护线程的生命周期是依赖于用户线程,当有用户线程存在,守护线程就会存活,当没有用户线程存在,那守护线程也随之消亡,
需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。

public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }
    public final boolean isDaemon() {
        return daemon;
    }

Priority:线程的优先级

线程优先级,顾名思义:就是来指导线程的执行优先级的
方法介绍:
int getPriority() 获取优先级
setPriority(int newPriority) 设置优先级
方法特点:
1、java线程的优先级并不绝对,它所控制的是执行的机会,也就是说,优先级高的线程执行的概率比较大,而优先级低的线程也并不是没有机会,只是执行的概率相对低一些。
2、Java线程一共有10个优先级,分别为1-10,数值越大,表明优先级越高,一个普通的线程,其优先级为5;
线程的优先级具有继承性,如果一个线程B是在另一个线程A中创建的,则B叫做A的子线程,B的初始优先级与A保持一致。
优先级范围:

public final static int MIN_PRIORITY = 1;  //最小优先级
public final static int NORM_PRIORITY = 5;  //默认优先级
public final static int MAX_PRIORITY = 10;  //最大优先级

java 中的线程优先级的范围是1~10.最小值是1,默认的优先级是5,最大值是10.“高优先级线程”会优先于“低优先级线程”执行

 public final int getPriority() {
        return priority;
    }
public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }
private native void setPriority0(int newPriority); //JNI
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值