学习笔记四:初识线程

1、什么是进程?什么是线程?

  • 什么是进程

进程是程序运行资源分配的最小单位;其中资源包括:CPU、内存空间、磁盘IO等,同一进程中的多条线程共享该进程中的全部资源。

  • 什么是线程?

线程是CPU调度的最小单位,必须依赖于进程而存在。

线程是进程的一个实体,是CPU调度、分派和能独立运行的基本单位,线程自己基本不拥有系统资源;

2、进程和线程有什么区别?

ea734afa80940119e3485caceb8e34d7fa8.jpg

3、Thread 类中的start() 和 run() 方法有什么区别? 

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程

4、线程的生命周期?

线程的生命周期有6种状态:

(1)新建/初始(new Thread):实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了新建(初始)状态

(未被启动),也就是说新生状态的线程有自己的内存空间,但是线程并没有运行,此时的线程不是活着的(not alive)

(2)运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。

          就绪(runnable):线程已经被启动,正在等待被分配给CPU时间片,获取cpu 的使用权,通过调用start()方法来启动线程进入就绪状态(ready),等待状态并不是执行状态,此时线程的状态是活着的(alive)

            运行(running):线程获得CPU资源正在执行任务(run方法),此时线程是活着的(alive)

   处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态

(4)阻塞(blocked):由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入阻塞状态 

  • 线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
  • 正在睡眠:用sleep(long t)方法使线程进入睡眠,睡眠着的线程在指定的时间过去之后,即进入就绪状态;
  • 正在等待:调用Object.wait()方法,可调用notify()方法唤醒,回到就绪状态;
  • 被另一个线程所阻塞:调用suspend()方法,可调用resume()方法恢复。

处于blocking状态的线程仍然是活着的(alive)

(5)超时等待(TIME_WAITING):该状态不同于WAITING,它可以在指定的时间内自行返回。

(6)终止(TERMINATED):表示该线程已经执行完毕。当线程执行完毕或者其他线程杀死,线程就进入死亡状态,这时线程不可能进入就绪状态等待执行

  • 自然终止:正常运行run()方法后终止;
  • 异常终止:调用stop()方法让一个线程终止运行;
  • interrupt:使用interrupt结束一个线程;
  • error/Exception异常导致终止。
  • 一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

4.1线程生命周期流转图

5、启动和终止线程

5.1 启动线程的三种方法

线程start()方法的含义:当前线程(即parent线程)同步告知Java虚拟机,只要线程规划器空闲,应立即启动调用start()方法线程

java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口

1)直接extends Thread 覆盖run()方法即可

2)实现Runnable接口,实现run()方法

3) implements Callable,实现call方法可以得到线程的执行结果

b7299632f3672f0624af475c7943c9d540e.jpg

启动 一个线程前,最后为这个线程设置线程名称,因为这样使用jstack分析程序或者进行问题排查时,会给开发人员一些提示,自定义的线程最好能够起个名字。

5.2 Thread初始化源代码解析

/**
 * Initializes a Thread with the current AccessControlContext.
 * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
 */
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
    init(g, target, name, stackSize, null, true);
}

/**
 * Initializes a Thread.
 *
 * @param g the Thread group
 * @param target the object whose run() method gets called
 * @param name the name of the new Thread
 * @param stackSize the desired stack size for the new thread, or
 *        zero to indicate that this parameter is to be ignored.
 * @param acc the AccessControlContext to inherit, or
 *            AccessController.getContext() if null
 * @param inheritThreadLocals if {@code true}, inherit initial values for
 *            inheritable thread-locals from the constructing thread
 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;
    //当前线程是该线程的父线程
    Thread parent = currentThread();
    //提供安全策略,它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager
           what to do. */
        if (security != null) {
            g = security.getThreadGroup();//获取线程组
        }

        /* If the security doesn't have a strong opinion of the matter
           use the parent thread group. */
        if (g == null) {//如果为空,使用父线程组
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
       explicitly passed in. */
    g.checkAccess();//检查访问是否有效

    /*
     * Do we have the required permissions?
     */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    //将父线程的ThreadLocal.ThreadLocalMap inheritableThreadLocals 复制过来
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* 分配一个线程ID */
    tid = nextThreadID();
}

5.3 理解线程中断

中断可以理解为线程的一个标志位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。

Thread类提供三种中断方法

(1)new Thread().interrupt()  :中断线程,但是无返回结果,是唯一    能将中断状态设置为true的方法;中断好比其他线程对该线程打了个招呼,其他线程通过interrupt()方法对其进行中断操作。

(2)new Thread().isInterrupted():判断当前线程是否被中断。

(3)Thread.interrupted():对当前线程清除中断状态,二次调用或者线程处于终结状态即使线程中断过,也是会返回false。

注意:阻塞的线程调用中断方法,会抛出InterruptedException异常

5.4 如何安全的终止线程

终止线程的策略:

  1. 中断状态是线程的一个标志位,而中断操作是一种简便的线程间的交互方式,这种方式最适合用来取消或者停止任务。
  2. 利用boolean变量来控制是否需要停止任务并终止该线程。

终止线程案例如下:

package com.black.example.helloworld.thread;

import java.util.concurrent.TimeUnit;

/**
 * 安全终止线程的两种方法
 * 1:interrupt
 * 2:volatile修饰的变量标志位
 */
public class ShutDown {
    public static void main(String[] args) throws InterruptedException {
        /**利用中断方式安全终止线程*/
        Runner runner = new Runner();
        Thread countThread = new Thread(runner,"CountThread");
        countThread.start();
        //睡眠1秒,main线程对countThread进行中断,使countThread能够感知中断而结束
        TimeUnit.SECONDS.sleep(1);//InterruptedException
        countThread.interrupt();//中断线程
        /**利用volatile修饰的变量安全终止线程*/
        Runner twoRunner = new Runner();
        countThread = new Thread(twoRunner,"booleanThread");
        countThread.start();
        //睡眠1秒,main线程对Runner two进行取消,使countThread能够感知on为false而取消
        TimeUnit.SECONDS.sleep(1);
        twoRunner.cancel();

    }

    private static class Runner implements Runnable{
        private long i;
        private volatile boolean on = true;
        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()){
                i++;
            }
            System.out.println("线程名称 = "+Thread.currentThread().getName()+",Count i = "+i);
        }

        public void cancel(){
            on = false;
        }
    }
}

运行结果:输出内容不同

fa88590a8717f6c063301c7e862d95544c0.jpg

6、线程的优先级

线程优先级:决定线程需要多或者少分配一些处理器资源的线程属性。

java线程中通过一个整形变量priority来控制优先级,优先级的范围从1~10,在线构建通过setPriority(int)方法来修改,默认优先级是5;

必须在调用satrt()之前设置优先级。

针对频繁阻塞的线程需要设置较高优先级;偏重计算的线程则设置较低优先级,确保处理器不会被独占;

有些操作系统会忽略优先级设定,表示程序的正确性不能依赖程序的优先级高地。

7、什么是守护线程 

守护线程:简单理解为后台运行线程,进程结束线程自然而然的结束,不需要手动关心其状态,java的垃圾回收也是一个守护线程

守护线程使用方法:new Thread().setDaemon(true),必须在启动线程之前设置。

构建Daemon时,不能依靠finally块的内容来确保执行关闭或者清理资源的逻辑。

8、常用方法之sleep/wait/notify解析

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

Object .....
public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException

Thread类之sleep方法

  • sleep()方法虽然会使当前线程停止执行,让出cpu给其他线程,但是不会释放自己的monitor对象(对象锁)以及监控状态,在中断结束后,依然能够保持代码继续执行。(类方法)

Thread类之yield方法

  • 暂停当前正在执行的线程,并执行其他线程;(类方法)
  • 使当前线程出让CPU执行时间,当并不会释放当前线程所持有的锁。执行完yield()方法后,线程从Running状态转变为Runnable状态,既然是Runnable状态,那么也很可能马上会被CPU调度再次进入Running状态。

Object类之 wait方法

  • 暂停当前正在执行的线程,直到调用notify()或notifyAll()方法或超时,退出等待状态;
  • wait方法的使用必须在同步的范围内,否则就会抛出IllegalMonitorStateException异常;
  • 调用wait方法后,会释放持有的monitor对象,并让自己处于等待状态;

Object类之 notify   notifyAll 方法

  • notify:唤醒在该对象上等待的一个线程;
  • notifyAll:唤醒在该对象上等待的所有线程;

sleep()和wait()方法都是暂停当前正在执行的线程,出让CPU资源。

方法所属类方法类型解除方法场景用途
sleepThread静态方法不释放锁timeout,interrupt无限制线程内的控制
waitObject非静态方法释放锁timeout,notify,interrupt同步语句块线程间的通信

转载于:https://my.oschina.net/zupengliu/blog/1843959

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值