多线程基础知识(线程创建、生命周期、相关方法、优先级)

1.线程的定义与创建

1.1线程的定义

每一个任务称作一个线程, 它是程序执行的最小单位;线程又叫做轻量级进程,可以把一个进程的资源分配和执行调度分开;线程是CPU调度的最小基本单位,各个线程既可以共享进程资源,又可以独立调度;各个线程拥有自己的栈空间

1.2线程与进程的区别

1)线程是CPU调度的最小基本单位;进程是资源分配的最小单位
2)进程有独立的地址空间,一个进程崩溃后在保护模式下不会对其他进程产生影响;线程没有独立的地址空间,只是一个进程中的不同执行路径,有自己的堆栈和局部变量,一个线程死掉就等于整个进程死掉。多进程的程序比多线程的程序健壮。
3)进程在执行过程中拥有独立的内存单元,而多个线程共享内存,多线程的程序运行效率更高,资源利用率高。
4)一个程序至少要有一个进程,一个进程至少有一个线程。考虑到安全性和稳定性的要求选择多进程;不同任务间需要大量共享数据或频繁通信时,需要频繁创建销毁时选择多线程。

1.3线程的创建方式

1)继承Thread类,重写run()方法
步骤:
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程

class Mythread extends Thread{
    @Override
    public void run() {
        .......
    }
}
public class ThreadTest01 {
    public static void main(String[] args) {
          Thread mythread = new Mythread();//创建线程对象
          mythread.start();//启动线程
         }
}

2)实现Runnable接口,重写run()方法
步骤:
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程

class  MyRunnable implements Runnable{
    @Override
    public void run() {
        ......
    }
}
public class ThreadTest02 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());//线程对象、


        thread.start();
    }
}

3)实现Callable接口,重写call()方法
步骤:
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "看电视";
    }
}
public class ThreadTest03 {
    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> future = new FutureTask<>(myCallable);
        Thread thread = new Thread(future);
        thread.start();
        try {
            System.out.println(future.get());//get()拿到call()结果
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

注:
Callable和Runnable接口的区别
实现Runnable接口重写run()方法,无返回值,不会抛出异常;创建Runnable实例,依此实例作为Thread的target来创建Thread对象,主线程必须等待run方法运行完成拿到结果
实现Callable接口重写call()方法,有返回值,返回值类型与泛型参数类型相同,且可以抛出异常;创建Callable实现类的实例,使用FutureTask类来包装Callable对象,将FutureTask实例对象作为Thread()的target来创建Thread对象,启动线程后,需要调用FutureTask类下的get()方法获取返回值

2.线程的生命周期

2.1线程的六状态介绍

2.1.1新建状态(NEW)

创建后尚未启动的线程:Thread thread1 = new Thread();

2.1.2就绪状态(RUNNABLE)

Runnable包括操作系统线程状态中的Ready和Running。处于就绪状态的线程有可能正在等待着CPU分配执行时间,有可能正在执行。

2.1.3阻塞状态(BLOCKED)

处于阻塞状态的线程等待监视器锁(monitor lock)进入同步代码块/方法( synchronized block/method)或者调用Object.wait方法后重新进入同步代码块/方法

2.1.4等待状态(WAITING)

处于等待状态的进程不会被分配CPU执行时间,等待被其他线程显示的唤醒。
线程处于等待状态是由于调用了没有超时时间的Object.wait(),或者没有超时时间的join()方法,或者LockSupport.park()方法。

2.1.5睡眠状态(TIMED_WAITING)

具有指定等待时间的等待线程的线程状态。
线程处于睡眠状态是由于调用了Thread.sleep(),或者有超时时间的Object.wait(),或者有超时时间的join()方法,或者LockSupport.parkUntil()方法,或者LockSupport.parkNanos()方法。

2.1.6终止状态(TERMINATED))

线程执行结束。
源码:

public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED;
}

2.2线程状态间的转换

在这里插入图片描述

3.线程中的常用方法

3.1start():线程启动

调用start()方法后,线程由新建状态转为Runnable就绪状态。

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();

3.2sleep():线程休眠

sleep()方法使当前线程指定毫秒级休眠,和wait()方法不同,sleep()方法不会放弃monitor lock(监视器锁)的使用权,调用sleep()方法会抛出异常
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
JDK1.5以后引入TimeOut枚举类型,对sleep()方法对其进行了封装 TimeOut.DAYS/HOURS/MINUTES/SECOND/MICROSECONDS/MILLISECONDS/NANOSECONDS.sleep(),省去时间换算问题。

//均为静态方法 调用方式Thread.sleep()
public static native void sleep(long millis) throws InterruptedException;

public static void sleep(long millis, int nanos) throws InterruptedException {
   ........
}

3.3yield():

向调度程序提示当前线程愿意放弃它当前使用的处理器。
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。调用yield方法让线程重回就绪状态。
A.yield(),会提醒调度器线程A愿意放弃本次的cpu资源,如果cpu资源不紧张,处理器有可能会忽略这种提示

public static native void yield();

sleep()和yield()方法的异同:
不同点:
①调用sleep()方法会抛出异常(可中断方法);调用yield()方法不会抛出异常(不可中断方法)
②调用sleep()方法会当前运行中的线程睡眠一段时间,将线程的执行权让给其它的线程,这段时间的长短是可以设置的;调用yield()方法使当前线程让出CPU执行权,但让出的时间是不可设置的
③调用sleep()方法进入睡眠状态;调用yield()方法进入就绪状态
相同点:
①sleep()方法和yield()方法都是Thread类方法
②sleep()方法和yield()方法都不会放弃锁

3.4join():

当前线程threadA里调用其它线程threadB的join方法,当前线程A进入WAITING/TIMED_WAITING状态,当前线程A不会释放已经持有的对象锁。线程B执行完毕或者millis时间到,当前线程进入就绪状态。join()方法可以控制线程的执行顺序。

public final synchronized void join(long millis) throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException
public final void join() throws InterruptedException

3.5实现线程中断的方法

a)interrupt():将java线程中的中断状态位置为true。线程A调用sleep()/join()/wait()(能抛出InterruptedException,是可中断方法),这些方法会使当前进入阻塞状态(操作系统层面),线程B调用被阻塞线程A的interrupt()方法会打断当前的阻塞状态,同时抛出异常(java.lang.InterruptedException)。对于正常运行的线程,调用interrupt()方法只是把线程A的状态改为interruptted,但是不会影响线程A的继续执行。

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();
}

b)isInterrupted():判断中断状态位是否是true



public boolean isInterrupted() {
    return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);

c)interrupted():静态方法。判断中断状态位是否是true;判断为true后擦除

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

3.6.wait/notify/notifyAll

wait()是Object的方法,放弃cpu,也放弃锁
obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

4.线程的优先级

private int  priority;
public final static int MIN_PRIORITY = 1; //最小优先级1
public final static int NORM_PRIORITY = 5;//默认优先级5
public final static int MAX_PRIORITY = 10;//最大优先级10
public final void setPriority(int newPriority){...}//设置优先级
public final int getPriority() {...}//获取优先级

优先级增加线程抢到CPU执行权的概率。线程优先级具有继承性。a线程设置优先级,然后启动b线程,b线程的优先级和a线程的优先级是一样的。
例如:

public static void main(String[] args) {
    Thread.currentThread().setPriority(10);
    Thread thread = new Thread(){
        @Override
        public void run() {
            super.run();
        }
    };
    thread.start();
    System.out.println(thread.getPriority());
    System.out.println(Thread.currentThread().getPriority());
}
//结果:10 10 

5、线程上下文切换

因为以下原因导致CPU不在执行当前的线程,转而执行另一个线程的代码
1、线程的CPU时间片用完了
2、垃圾回收
3、有更高优先级的线程需要运行
4、线程自己调用了sllep、yield、wait、join、park、synchronized、lock等方法
当线程的上下文切换发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,他的作用是记录下一条JVM指令的执行地址,是线程私有的。上下文切换频繁会影响性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值