多线程介绍(归纳)

线程和进程

进程就是计算机中正在进行的一个独立的应用程序,进程是一个动态的概念必须是进行状态,如果一个应用没有启动,那就不是进程
线程就是组成进程的基本单元,可以完成特定的功能,一个进程可以有多个线程组成

进程和线程的区别:

1、内存空间上的区别:
进程是有独有的内存空间,每个进程之间是相互独立的,互不影响
线程有共享的内存空间(也有私有空间)
2、安全性
进程是相互独立的,一个进程的奔溃不会影响到其他的进程,进程是安全
线程存在内存空间共享,一个线程的奔溃可能会影响到其他的线程的执行,线程的安全是不如进程

  • 关系:进程是相互独立的,一个进程下可以有一个或者多个线程

Java中是很少使用进程概念,但也可以使用
//Java中创建进程方式
Runtime runtime = Runtime.getRuntime();
Java启动线程
private native void start0();
native方法会调用底层操作系统支持,Java本身是没办法启动线程的,线程启动是需要依赖底层操作系统支持的,Java调用native方法,调用底层操作系统C++编写的动态函数库,有C++去操作底层启动线程。Java是间接的调用来启动线程

线程创建方式

● 继承thread类
● 实现Runable接口
● 实现Callable接口

实现Runable接口

Runable接口定义如下:

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

Runable接口中提供了一个抽象的run方法,实现RUnable接口实现run方法

Runnable实现方式

/**
 * 通过实现Runnable接口来进行run方法
 */
public class RunableDemo implements Runnable {
    @Override
    public void run() {
        System.out.println("线程名:"+Thread.currentThread().getName()+"的线程在执行");
    }
}


RunableDemo runableDemo = new RunableDemo();
Thread thread = new Thread(runableDemo);
//当前才会创建子线程
thread.start();

Runnable实现类是任务,Thread类是线程对象,线程是用来执行任务的

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

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

继承Thread类

Thread类的定义

  • public class Thread implements Runnable
    可以看出:Thread类实现了Runnable接口,即Thread类也是Runnable接口实现类,在Thread中业余run方法的实现`
    public void run() {
        if (target != null) {
            target.run();
        }
    }

Thread类中的run方法的实现中,target任务体不能为空,即调用run方法,需要通过Thread类来重写run方法

继承Thread类使用Demo

/**
 * 通过继承Thread类来重写run方法
 */
public class ThreadDemo extends Thread {
    @Override
    public void run() {
        System.out.println("ThreadDemo类 线程名:"+
                Thread.currentThread().getName()+" 在执行");
    }
}


        //第二种:通过继承Thread类来创建线程
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();

继承Thread类创建线程步骤:

  1. 创建类,继承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> {
    //该类能接口Callable实现类
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
}


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

FutureTask类是继承自Runnable接口,即FutureTask是Runable接口的实现类,而FutureTask类提供构造函数FutureTask(Callable callable),可以接受Callable类型的任务
就可以借助FutureTask将Callable类型的实例转化为Runnable类型的实例

Callable接口实现的Demo

public class CallableDemo implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("CallableDemo类 线程名:"+Thread.currentThread().getName()+"在执行");
        return null;
    }
}

      //实例化Callable接口实现类
        CallableDemo callableDemo = new CallableDemo();
        //通过FutureTask可以将Callable实例转化为Runable实例
        FutureTask <String> futureTask = new FutureTask <String>(callableDemo);
        Thread thread1 = new Thread(futureTask);
        thread1.start();

实现Callable接口创建线程步骤

  1. 创建类,实现Callable接口,实现其提供的call方法
  2. 创建Callable实例
  3. 创建FutureTask实例,将Callable实例作为参数传入
  4. 创建Thread实例,将FutureTask实例作为参数传递(当做Runable实例)
  5. 启动子线程,调用Thread类的Start方法
  6. 线程的状态及状态转换

线程状态

JDK中提供的线程状态在Thread类中的一个子枚举类
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}

  • 新建状态(NEW)
    使用new语句创建的线程处于新建状态(new Thread),仅仅在堆上分配了内存
  • 就绪状态(Runnable)
    当线程创建后,其他线程调用start方法,该线程就进入到就绪状态,JVM就会为创建方法调用栈和程序计数器,处于这个状态的线程位于可运行的池中,等待获取CPU的使用权
    其他处于则塞状态解除阻塞之后也会进入就绪状态
  • 运行状态(Running)
    处于这个状态的线程占用CPU,执行程序代码,只有处于就绪状态的额线程才会有机会转到运行状态
  • 阻塞状态(Blocked)
    当线程由于缺少响应的资源而导致程序无法继续执行,就会从运行状态进入到阻塞状态比 如:锁资源,IO资源
  • 等待状态(Waiting)
    当线程调用了wait方法就会进入到waiting状态,该状态只有notify等操作才能唤醒线程进入下一个状态
  • 睡眠状态(Time_Waiting)
    如果线程执行了sleep(long)/join(long)/wait(long),会触发线程进入到Time_waiting状态,只有到达设定的时间,才会脱离阻塞状态
  • 终止状态(Terainated)
    当线程退出run方法,就进入到终止状态,该线程结束生命周期

线程状态转化
在这里插入图片描述
一个线程的生命周期需要状态:New、Runnable、Running、Terminated状态
线程在需要响应资源时,就会进入到阻塞状态,阻塞状态包含Waiting、Blocked、Time_waiting状态

线程方法

start():启动线程
start方法作用是启动一个新线程执行,start需要首先调用,而且不能不能重复调用
start方法会使线程从新建状态进入到就绪状态

public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }

    private native void start0();

start方法启动子线程是通过调用系统提供的方式来启动线程
run():子线程的执行体
run方法和普通成员方法一样,可以重复调用
单独调用run方法,会在当前线程中执行run方法,并不会启动新线程
yield():线程让步
yield方法暂停当前线程的执行,并让步于其他同优先级的线程,让其他线程先执行

特点:

  1. yield方法的让步,能让正在执行的线程由“运行状态”进入到“就绪状态”,等待调度执行
  2. yield仅仅是让出CPU资源,让给谁,是有系统决定的,系统会让其他相同优先级或者是更高优先级的线程来获取执行权,要是没有更高优先级的线程,会继续执行原来的线程

public static native void yield();

是定义在Thread类中的静态方法
join():线程合并
作用:
暂停当前线程的执行,等待子线程执行,也叫做线程合并,join方法做的事情就是讲并行执行的线程合并为串行执行
例如:如果在a线程中调用b线程的join方法即b.join方法,则会让b线程先执行,直到b线程执行结束,a线程才能继续执行

方法介绍:
join方法是在Thread类中,可以被中断掉
t.join()是允许t线程插队到当前线程前面,等t线程执行完成再执行当前线程
t.join(millis)是允许t线程插队到当前线程前面,等t线程执行且最长等待millis毫秒再执行当前线程
t.join(millis,nanos)和t.join(millis)类似,给定了有限时间等待,只是提供了一个纳秒级精度

public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            //特殊标识,标识一致阻塞
            while (isAlive()) {
                wait(0);
            }
        } else {
            //millis大于0,会阻塞到最多millis时间
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    public final synchronized void join(long millis, int nanos)throws InterruptedException {
        if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}
        if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        join(millis);
    }


    public final void join() throws InterruptedException {
        join(0);
    }

方法特点:

  1. 当前线程a调用b.join(),b线程会开始执行,a线程进入到阻塞状态
  2. join方法hi被中断,如果有Interrupt方法会导致当前阻塞提前结束,会抛出InterruptedException
    sleep():线程休眠
    会是当前线程进入休眠

特点:在sleep期间,线程运行状态从运行进入到阻塞状态,会让出CPU的执行权,当线程被重新唤醒时,会由阻塞状态进入到就绪状态,等待CPU的使用权

方法介绍:
sleep方法主要有两个,sleep(long millis) 和sleep(long millis, int nanos)两个方法功能类似,后一个方法提供更高精度的纳秒级控制
sleep方法是Thread里提供的静态方法,当前的方法也是可以抛出InterruptedException,可以被Interrupt中断掉

public static native void sleep(long millis) throws InterruptedException;

    public static void sleep(long millis, int nanos)throws InterruptedException {
        if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}

        if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

Interrupt():中断线程
Interrupt方法是中断当前线程,终止处于阻塞状态的线程

Interrupt方法在Thread类中,是一个普通方法,可以都对象来调用

    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
private native void interrupt0();

interrupt特点
interrupt方法调用仅仅修改了一个中断标志
如果当前线程是可中断的状态(调用了sleep、join、wait等方法导致线程进入阻塞状态)在其他线程中调用interrupt方法,会立即抛出一个InterruptedException,来停止掉中断
如果当前处于运行状态,调用interrupt方法,线程会继续执行,直到发生sleep、join、wait等方法的调用,才会进入阻塞

deamon:守护线程
守护线程存在两个方法
setDaemon(boolean on) 设置守护线程,参数是true和false,true表明设置为守护线程, false,设置为非守护线程 ,默认是false
boolean isDaemon()判断是否守护线程,返回Boolean类型

守护线程:Java线程有两种,一种是守护线程,一种是用户线程
用户线程一般用户执行用户级任务,一般创建的线程都是用户线程
守护线程也叫做“后台线程”服务于用户线程,一般就是用来执行后台任务,例如:垃圾回收是专门线程来处理的,负责垃圾回收的线程就是守护线程

守护线程的生命周期:
守护线程的生命周期依赖于用户线程,当用户线程存在,守护线程就会存在,当用户线程不存在,那么守护线程也就会随之消亡
Priority:线程优先级
线程优先级,就是用来指导线程执行的优先权

方法介绍:
int getPriority():获取线程的优先级setPriority(int newPriority),设置当前线程的优先级,newPriority必须是1-10之间的额整数,否则会抛出异常
优先级范围:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
设置Java线程的优先级,最小值为1,默认值是5,最大值是10

方法特点:

  1. 线程的优先级并不绝对,所控制的是执行的机会,优先级高的线程执行的概率是比较大,而优先级低的线程也并不是没有机会,只是执行的概率会低一些
  2. 优先级工10个优先级,分别为1-10,数值越大,表名优先级越高,普通的线程,优先级是5
  3. 线程的优先级是用来指导JVM层面优先那个线程执行,最终执行顺序需要操作系统来指定
  4. 注意;代码最好不要依赖于优先级线程,有可能会造成问题,java中给定的优先级并不一定严格按照给定优先级执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值