多线程介绍

课程内容

1.线程介绍:进程、线程概念、线程的创建方式、线程状态及状态转换关系、线程方法

2.java内存模型:并发特征:原子性、有序性、可见性...并发和并行、临界区等

3.volatile关键

4.Synchronized关键字

5.线程间通信:notify、notifyAll和wait方法

6.生产者消费者模式

推荐书籍:

《Java核心技术卷1》第14章

线程介绍:

线程和进程:

进程就是计算机正在执行的一个独立的应用程序,进程是一个动态的概念,必须是进行状态,如果一个应用程序没有启动,就不是进程

线程是组成进程的基本单位,可以完成特定的功能,一个进程由一个或多个线程组成

进程和线程的区别:

1.内存空间的区别

进程有独立的内存空间,每个进程之间是相互独立的,互不干扰的

线程有共享的内存空间

2.安全性:

进程是相互独立的,一个进程的崩溃不会影响到其他的进程

线程内存共享的,一个线程的奔溃可能会影响到其他的线程的执行,线程的安全性不如进程

关系:

进程是相互独立的,一个进程可以包含一个或多个线程

Java中很少提到进程概念,但也可以使用:

Runtime.getRuntime();

Java中创建线程方式:

Java中多线程的创建需要调用start方法创建出新线程

private native void start0()

start底层调用native修饰的start0的方法,native需要调用操作系统来执行,也就是Java创建多线程的方式需要借助于底层操作系统的线程创建,线程的创建交给底层C++的线程创建方式,java是对底层线程创建的封装

线程的创建方式:

继承Thread类

实现Runnable接口

实现Callable接口

1.实现Runnable接口

Runnable接口定义如下:

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

通过接口定义可知,通过Runnable接口来实现多线程就需要实现该接口中的run方法

public class RunnableDemo implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":正在执行...");
    }
}
public static void main(String[] args){

//执行的任务体
        RunnableDemo runnableDemo = new RunnableDemo();
        //将任务体提交给线程,获取线程实例
        Thread thread = new Thread(runnableDemo);
        //启动多线程,调用start方法
        thread.start();
        System.out.println(Thread.currentThread().getName()+":main方法所在类正在执行");
}

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

1.创建一个特定类,必须实现Runnable接口,并实现run方法

2.实例化特定类对象

3.创建Thread类对象,将特定类对象作为参数传递

4.启动子线程,调用Thread的类的start方法

2.继承Thread类

Thread类的定义

public class Thread implements Runnable{
  private Runnable target;
  
  public void run() {
        if (target != null) {
            target.run();
        }
    }
}

Thread类本身实现了Runnable的接口,即Thread类是Runnable的实现类,Thread类也有run方法,run方法来执行任务体target,需要来重写run方法

继承自Thread类的Demo:

/**
 * 继承自Thread类
 */
public class ThreadDemo extends Thread {

    //重写run方法
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":子线程执行...");
    }
}


public static void main(String[] args){
 //继承自Thread类
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
        System.out.println(Thread.currentThread().getName()+":main方法所在类正在执行...");
}

继承自Thread类创建线程的方式:

1.创建特定类,继承自Thread类,重写run方法

2.实例化创建类的对象

3.调用对象的start方法启动子线程

3.实现Callable接口

Callable接口声明如下:

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

callable接口提供了call方法,具有返回值,通过泛型定义(控制返回值),该接口可以抛出异常

callable接口的不能直接使用,需要借助FutureTask类,FutureTask类可以接受Callable接口的实现类

FutureTask类定义如下:

public class FutureTask<V> implements RunnableFuture<V>{
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
}

public interface RunnableFuture<V> extends Runnable, Future<V>

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

可以实现将Callable类型实例转化为Runnable类型实例,交给Thread类处理

Callable接口的实现类Demo

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

public static void main(String[] args){
//实例化Callable接口实现类
        CallableDemo callableDemo = new CallableDemo();
        //FutureTask类实例
        FutureTask <String> futureTask = new FutureTask <>(callableDemo);
        Thread thread1 = new Thread(futureTask);
        thread1.start();
}

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

1.创建特定类,实现Callable接口,实现该接口call方法

2.创建特定类的实例

3.创建FutureTask实例,将Callable实例作为参数传入

4.创建Thread对象实例,将FutureTask实例作为参数传入(当做Runnable实例)

5.启动子线程,调用Thread类对象的start方法

4.匿名内部类形式实现

public static void main(String[] args){
       new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":匿名内部类实现...");
            }
        }).start();
        System.out.println(Thread.currentThread().getName()+":main方法所在类正在执行...");
    }

注:线程的实现也可以是匿名内部类方式,可以归结为实现Runnable接口

Runnable接口和Thread类区别?

1、线程类继承自Thread则不能继承其他类(单继承),而Runnable接口可以

2、继承自Thread类相对于Runnable接口实现,创建线程的方法更简单

Callable接口和Runable接口的区别:

1、Callable接口方法为call(),而Runable接口方法为run()

2、Callable接口是有返回值,而Runable的接口的方法是不能返回值

3、call()方法是可以抛出异常的,run()是不能抛出异常

4、运行Callable任务可以拿到FutureTask的对象,通过该对象可以对子线程进行操作,获取结果.

线程状态及其转换:

线程状态:

在JDK中的Thread类中有一个子枚举类State:定义线程存在状态

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

在JDK中线程的状态有6种

新建状态(NEW)

用new语句创建的线程就处于新建状态,和Java对象一样,仅在堆中分配了内存,在调用start之前,就处于新建状态

就绪状态(Runnable)

在一个线程对象创建之后,调用它的start方法,该线程就进入就绪状态,JVM为线程创建栈和程序计数器。处于这个状态的线程位于可运行池中,等待获取CPU的执行权

运行状态(Running)

处于就绪状态的线程获取了CPU的执行权执行程序代码,只有处于就绪状态的线程才有机会到运行状态

阻塞状态(BLOCKED)

在线程期望进入同步代码块或者同步方法中时会进入该状态(Synchronized或者Lock锁),等待资源(非CPU资源),等待到了资源线程就会进入就绪状态等待CPU调度

等待状态(WAITING)

一般是调用wait方法会进入等待状态,当线程被其他的调用notifyAll或者notify的线程通知唤醒,才会从等待状态进入阻塞状态(还需要获取锁)

超时等待(TIMED_WAITING)

如果线程执行了sleep(long time),wait(long time),join(long time),设置等待时间,当设定的等待时间到了,就会脱离阻塞状态

终止状态(TERMINATED)

线程退出run()方法时,就进入终止状态,该线程结束生命周期

线程状态转换:

一个线程的生命周期中需要状态:New、Runnable、Running和Terminated四个状态。当线程需要相应资源时,进入到阻塞状态,阻塞状态包含Blocked、Waiting、和Timed_Waiting状态

线程方法

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方法启动子线程是通过调用native的系统提供的方式来启动线程

run():子线程执行体

作用是将所有的子线程业务逻辑在run方法中实现,在调用start方法时,会启动子线程并自动调用到run方法。不需要显性调用。

单独调用run()方法,会在当前线程中执行run()方法,并不会启动新线程

yield():线程让步

作用:是暂停当前的线程的执行,并让步于其他同优先级或更高优先级线程,让其他线程先执行

public static native void yield();

方法特点:

1、yield方法是Thread类的静态方法,通过Thread.yield()调用

2、能够让正在执行的线程由“运行状态”进入到“就绪状态”,等待CPU调度。

3、正在执行线程让步于相同优先级或更高优先级的线程来获取CPU执行权,当同级优先级或者更高优先级没有对应线程时,当前线程又可以继续执行

sleep():线程休眠

作用:让线程休眠,那个线程调用sleep方法,那个线程休眠

方法介绍如下:

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

sleep方法有两个,分别是sleep(long millis)和sleep(long millis, int nanos),两个方法功能一样,第二个方法提供了纳秒级的时间控制

特点:

1、sleep方法是静态的native方法,调用Thread.sleep()调用

2、sleep方法调用会使当前线程从“运行状态”进入到“睡眠状态”,直到给定的时间millis,当前线程才会被唤醒

3、sleep方法会使线程休眠到指定的时间,如果要提前终止休眠,可以通过Interrupt方法调用,会使当前的sleep方法抛出InterruptedException异常,结束掉休眠

join:线程合并

作用:暂停当前线程,等待子线程执行结束当前线程才能执行。join方法让并行的线程合并为串行的线程执行

例如:a线程中执行代码b.join()方法,则a线程会停止当前执行,并让b线程线执行,直到b线程执行结束,a线程才继续执行

join方法实现:

//指定合并等待时间
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 {
            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、线程方法提供了三种方法join(long millis)、join(long millis, int nanos)和join()方法,都是调用了join(long millis)的实现

2、join方法定义在Thread类中,可以抛出InterruptedException异常,即join方法是可以被中断合并

3、join方法调用会使线程状态从“运行状态”进入到“阻塞状态”

4、join方法可以使线程按照顺序串行执行

Inturrupt():中断线程

作用:中断当前线程,中断处于阻塞状态的线程

方法:

boolean isInterrupted()  //返回值:true:当前线程被中断  false:当前线程未被中断
void 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()方法底层调用的是native的interrupt0()方法,发起中断操作仅仅修改中断标志位

1、如果当前的线程处于阻塞状态(sleep、join、wait等方法会导致线程进入阻塞状态),在任意的其他线程调用Interrupt()方法,那么线程会立即抛出一个InterruptedException退出阻塞状态

2、如果当前的线程处于运行状态,其他线程调用Interrupt方法,当前线程继续运行,直到发生了阻塞之后随后会立即抛出异常跳出阻塞

deamon:守护线程

方法介绍:

void setDaemon(boolean on)  //设置守护线程, 参数Boolean类型 true:设置为守护线程 false:非守护线程 默认是false
  boolean isDaemon()  //判断当前线程是否为守护线程

守护线程:

Java中有两种线程,用户线程(非守护线程)和守护线程,通过isDaemon方法进行区分,如果返回是false:说明线程是用户线程,否则是”守护线程“

用户线程一般用户执行用户级的任务

守护线程也称之为”后台线程“,服务于用户线程,一般执行后台任务,例如:垃圾回收的线程是单独线程来处理的,负责垃圾回收的线程就是守护线程

线程生命周期:

守护线程是依赖于用户线程,当用户线程存在时,守护线程就会存活,当没有了用户线程时,那么守护线程也就会随之消亡

Priority:线程优先级

就是用来指导线程的执行优先级的,指的是用来告诉CPU哪些线程优先被执行

方法介绍:

void setPriority(int newPriority) //设置当前线程的优先级,newPriority必须是1~10之间的整数,否则会抛出异常
int getPriority() //获取当前线程的优先级
 public final static int MIN_PRIORITY = 1; //最小优先级
 public final static int NORM_PRIORITY = 5;//默认优先级
 public final static int MAX_PRIORITY = 10;//最大优先级

优先级的特点:

1、Java的线程的优先级并不是绝对的,其控制的是执行的机会,优先级高的线程被执行的概率比较大,而优先级低的线程也并不是没有机会,只是执行的概率相对低一些

2、Java线程的优先级一共10个级别,分别为1-10,数值越大,表明优先级越高,一个普通的线程,其优先级为5(即为默认优先级)

线程调度:

方法级的调度:

Java线程中提供的一些方法可以进行线程的调度,合理的线程方法使用,可以充分发挥系统的性能,提高程序的执行效率

1.线程的优先级:高优先级线程获得较多的运行机会

2.线程睡眠:sleep(),让当前线程从运行状态进入到阻塞状态

3.线程让步:yield(),让正在执行的线程从运行状态进入就绪状态

4.线程合并:join(),当前调用join方法的线程进入到运行状态

5.线程等待:wait(),会使线程从运行状态进入到等待状态

系统级的调度:

主要指系统在特定的时机自动进行调度,主要涉及调度算法

FIFO:先进先出算法

SJF:最短作业优先算法

SRTF:最短剩余时间优先算法

RR(Round Robin):时间片轮转算法

HPF(Higest Priority First):最高优先级算法

OS系统调度算法一般都是基于时间片的优先级算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值