多线程——线程基础

一、线程和进程的概念

1.概念

  1. 进程指的一段正在运行的独立的应用程序,进程是程序执行过程中资源分配和管理的基本单位。进程有独立的空间。相对于线程安全性更高
  2. 一个程序运行中可以执行多个任务,任务称之为线程。线程是组成进程的基本单元,每一个线程完成特定的功能。线程是cpu执行的最小单位。 线程存在共享的空间

2.多线程和多进程的区别:

  1. 每个进程拥有自己独有的数据线程共享数据,线程之间的通信相比于进程之间的通信更有效更容易
  2. 线程相比于进程创建/销毁开销更小
  3. 进程是资源分配的最小单位,线程是cpu调度的最小单位
  4. 多进程程序更加健壮,多线程程序只要有一个线程挂掉,对其共享资源的其他线程也会产生影响。
  5. 如果追求速度,或者频繁创建和销毁,选择线程。因为进程创建和销毁开销很大(需要不停的分配资源)。如果追求系统更加稳定,选择进程。线程是轻量级的进程。

二、线程的创建

1.继承Thread 类

需要重写Thread类的Run方法,然后调用start()方法启动线程,启动线程后执行的是run方法。

  • 创建线程的步骤
  1. 创建一个类继承Thread类,重写run()方法,将所要完成的任务代码写进run()方法中;
  2. 创建Thread类的子类的对象
  3. 调用该对象的start()方法,该start()方法表示先开启线程,然后调用run()方法。
public class TestThread extends Thread{
 
    @Override
    public void run() {
        for(int i=1; i<=3; i++) {
            System.out.println(this.getName());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.Runnable 实现

需要重写run方法,然后将这个实现类当做参数传给Thread类

  • 创建线程的步骤
  1. 创建一个类并实现Runnable接口;
  2. 重写run()方法,将所要完成的任务代码写进run()方法中;
  3. 创建实现Runnable接口的类的对象,将该对象当做Thread类的构造方法中的参数传进去;
  4. 使用Thread类的构造方法创建一个对象,并调用start()方法即可运行该线程。
public class TestRunnable implements Runnable{
    @Override
    public void run() {
        int times = 5;
        for(int i=0; i<times; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(100);
            }
            catch(InterruptedException ie){
                ie.printStackTrace();
            }
        }
    }
}

3.Callable 实现

  • 创建线程步骤
  1. 创建Callable接口的实现类重写call方法
  2. 创建Callable实现类的实例,使用FutureTask包装该实例
  3. .将FutureTask实例作为参数创建线程对象
  4. 启动该线程
  5. 调用FutureTask的get方法获取子线程的执行结果
public interface Callable<V>{
	V call() throws Exception;
}

接口存在Executor框架中类,相比于Runnable更加强大:

  1. Callable可以在任务执行结束之后提供一个返回值
  2. call方法可以抛出异常
  3. 运行Callable任务可以拿到一个Future对象,Future提供get方法

4.匿名内部类

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <20 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        }).start();

三、线程的生命周期及常用方法解析

(一) 线程基本状态

1.new(新建状态)

当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

2.Runnable(就绪状态)

当调用线程对象的start()方法,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了x.start()此线程立即就会执行。

3.Running(运行状态)

4.Blocked(阻塞状态)

处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。

5.Waiting(等待状态)

等待一个监视器锁进入同步代码块或者同步方法,代码块/方法某一时刻只能够有一个线程执行,其他线程只能等待。

6.Timed_Waiting(睡眠状态)

sleep() 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。

7.Terminated(终止状态)

是一个线程的最终状态,线程进入到Terminated状态,意味着该线程的生命周期结束了,下面这些情况都会使得线程进入Terminated状态

  1. 线程执行正常结束
  2. 线程运行出错意外结束
  3. JVM crash

(二)常用方法

start() 启动线程

启动一个线程,将线程添加一个线程组中,线程状态会从New状态转换到Runnable状态,然后获取Cpu之后进入Running状态执行run

run() 子线程执行体

yield() 线程让步

让步于相同优先级或更高优先级的线程来执行。yield表示提醒cpu调度器我愿意放弃当前的cpu资源,(属于启发式方法),如果cpu资源不紧张的情况下,会忽略这种提示

  • yieldsleep区别
  1. jdk1.5之前,yield调用了sleep
  2. sleep使得当前线程暂停执行,不会占用cpu资源,yield只是对于cpu调度器的一个提示
  3. sleep会导致线程短暂的阻塞,yield会使得线程Runnable -> Runnable
  4. sleep会捕获到中断异常,yield不会捕获到中断异常

sleep() 线程睡眠

sleep是一个静态方法,其中存在两个重载函数

public static native void sleep(long millis)                   
public static void sleep(long millis, int nanos)  

使得线程进入睡眠状态,暂停执行,sleep不会放弃monitorlock的所有权。jdk1.5之后,jdk引入一个枚举TimeUnit,其对sleep方法做了封装,直接使用从而时间单位
换算的步骤,比如线程睡眠 3h 27min 8sec 88 msec

join() 线程同步

让线程进行串行处理。在线程B中join某个线程A,会使得B线程进入等待,直到线程A结束生命周期,或者达到给定的时间,在这期间线程B会处于等待状态

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值