初识多线程

目录

线程定义

线程的创建

线程的操作

1. 构造方法

2. 常见属性

3. 启动一个线程

4. A线程阻塞式等待B线程停止

5. A线程通知B线程停止(中断线程)

6. 获取当前线程引用

7. 休眠当前线程

线程的状态


线程定义

要谈及线程,必须要理解进程这个概念。百度百科是这样描述的:

确实是有点抽象,但是当我们打开任务管理器时,每一个在操作系统中运行的exe程序都是一个进程,进程是受操作系统管理的基本运行单元。

那么什么是线程呢?线程可以理解为在进程中独立运行的子任务。比如上面的360.exe运行时就有很多子任务在同时运行。清理垃圾、病毒查杀、电脑瘦身等等,这些不同的任务都可以“同时”运行,其中每一项任务都可以理解为多个“线程”在工作。

为什么要使用多线程进行工作呢?主要有两个原因:

          1. 可能提高执行速度。

在做A的同时,还可以执行B。 显然在某些情况下可以提高执行速度。但是线程的创建是有成本的,如果创建线程耗费的时间大于节省的时间,则不会提高速度。

          2. 某些 场景下,必须多线程才能处理,例如有阻塞IO时(一个调度单位失效了,剩下的调度单位还可以抢夺CPU)

线程的创建

线程创建就是1. 创建一个线程对象 2. 调用该对象的start()方法,把对象放入就绪队列。主要有两种方式:

1. 直接创建线程对象

public class MyThread extends Thread {
    @Override
    public void run() {
        //线程执行的代码
    }

    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();// 把 thread 线程放入就绪队列
    }
}

2. 先创建Runnable对象,再创建线程对象。Runnable代表的是要求一个线程执行的任务清单对象。

    public static void main1(String[] args) {
        // 创建一个目标对象,具体要做的实现,见 run 方法
        Runnable target = new MyRunnable();
        // 拿着目标对象,去创建线程对象,这个线程要干的活,就是目标对象中指定的
        Thread b = new Thread(target);     
        thread.start();
    }

 3. 三种变形

    private static void useNotHaveNameClass() {
        // 等同于直接创建线程对象
        Thread a = new Thread() {
            @Override
            public void run() {
                // 这里指定线程要干的活儿
            }
        };

        // 等同于先创建目标对象,再创建线程对象
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                // 这里指定线程要干的活儿
            }
        });

        // b 的变形,使用 lambda 表达式
        // 完全等于 b 的写法
        Thread c = new Thread(() -> {
            // 这里指定线程要干的活儿
        });

在这里,我们要注意的是,我们执行thread.start(); 之后,只是把创建出来的线程放进了就绪队列中,并不意味着该线程会马上抢到CPU,更不意味着要执行该线程的代码。当:1. CPU现在的线程被调度下来了 2. t被从就绪队列中选中了 3. t被调度上CPU上了。 只有这样,该线程的代码才被执行。

因为Thread这个类也实现了Runnable接口。所有我们可以吧一个Thread对象看成一个Runnable对象。

        Thread t = new MyThread();
        Runnable target = t;
        Thread thread = new Thread(target);

线程的操作

1. 构造方法

构造方法
方法说明
Thread()创建线程对象
Thread(Runnable target)
使用 Runnable 对象创建线程对象
Thread(String name)
创建线程对象,并命名
Thread(Runnable target, String name)
使用 Runnable 对象创建线程对象,并命名

2. 常见属性

属性
1. ID : ID是每个线程唯一的标识符。获取方法 : getId()
2. 名称 :为了方便Debug,因此我们可以给线程一个名称。 获取方法:getName()
3. 状态 :可以获取线程的状态。获取方法:getState()
4. 优先级 :这里的优先级是建议性的优先级,不要求线程强制遵守。 获取方法:getPriority()
5. 是否后台线程 :JVM会在一个进程的所有非后台线程结束后,才会结束运行。获取方法:isDaemon()
6. 是否存活: 简单的理解,为 run 方法是否运行结束了。获取方法:isAlive()
7. 是否被中断 :isInterrupted()

3. 启动一个线程

启动一个线程 :start() 方法。把线程对象放入就绪队列中,使得拥有被调度的资格。并不意味着会立刻执行该线程所要执行的任务。

那么 start() 方法和 run() 有什么区别呢?

覆写 run() 方法的目的,只是指导线程应该进行的工作,没有其他作用。

start() 方法不需要覆写,而调用 start() 方法,则会把线程放到就绪队列中,是线程用于被调度的机会,即就是抢CPU的机会。

4. A线程阻塞式等待B线程停止

在 A 线程执行的代码中,调用 b.join(); 会造成两个后果:

            1. A 线程主动放弃 CPU

             2. 直到 B 结束之前,再也不会抢 CPU。A 线程会阻塞在那里,直到 B 线程执行结束,才接着往下执行

5. A线程通知B线程停止(中断线程)

A 线程通知 B 线程停止。主要有两种方法。方法1可以设置共享的标记进行沟通,比如设置一个boolean类型的进行标记,状态改变,线程停止。不过这种方式存在一定的缺陷,当子线程中有 sleep() 等操作时,无法实时进行响应。主要看第二种方法,我们可以使用线程原生提供的方法,Thread中的一些方法。

 通过 thread 对象调用 interrupt() 方法通知该线程停止运行 ,thread 收到通知的方式有两种:

         1. 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志

         2. 否则,只是内部的一个中断标志被设置, thread 可以通过下面的方法查看中断标志是否被设置:
                     1. Thread.interrupted() 判断当前线程的中断标志被设置, 清除中断标志
                     2. t.isInterrupted() 判断指定线程的中断标志被设置, 不清除中断标志
显然第二种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
 

6. 获取当前线程引用

public static Thread currentThread();    //返回当前线程对象的引用

用法如下:

public class ThreadDemo { 
    public static void main(String[] args) {
        Thread thread = Thread.currentThread(); 
        System.out.println(thread.getName());
 } 
}

7. 休眠当前线程

public static void sleep(long millis) throws InterruptedException休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠
因为线程的调度是不可控的,所以,这个方法 只能保证休眠时间是大于等于休眠时间的。 用法如下:
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
    System.out.println(System.currentTimeMillis());
    Thread.sleep(3 * 1000);
    System.out.println(System.currentTimeMillis());
    }
}

线程的状态

线程的状态用来描述当前线程处于一个什么地步,进行JVM可以根据当前该线程的转态决定下一步该做什么。

我们可以先查看线程都有什么状态:

public class ThreadState { 
    public static void main(String[] args) { 
        for (Thread.State state : Thread.State.values()) { 
            System.out.println(state);
        }
    } 
}

输出的结果:NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED

1. NEW:是线程刚刚被创建的状态。

2. RUNNABLE:
       Ready:该线程拥有抢CPU资格
       Running:该线程已经在CPU上了
从Running到Ready可能会有三种情况造成:
             1. 被高优先级抢走了
             2. 主动放弃 -yield()
             3. 时间片耗尽

3. TERMINATED:线程的有效代码已经执行结束,但线程对象还在。

4.  BLOCKED WAITING TIMED_WAITING:不再拥有抢CPU的资格。

要注意:除了NEW和TERMINATED,线程都是存活的,即isAlive()都返回true。

用一副线程的状态转移图可以形象描述线程的状态转移:

 okay。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值