一个线程两次调用start()方法会出现什么情况?谈谈线程的生命周期和状态转移。

java线程不允许启动两次。会抛出异常。多次调用start被认为是编程错误

java5之后的线程生命周期:

  • 新建(new)
  • 就绪(Runnable)
  • 运行 (Running)
  • 阻塞 (blocking)
  • 等待(waiting)
  • 结束(Terminated)

各种状态切换的场景:

1.RUNNABLE 与 BLOCKED 的状态转换

线程等待 synchronized 的隐式锁。

等待的线程就会从 RUNNABLE 转换到 BLOCKED 状态。而当等待的线程获得 synchronized 隐式锁时,就又会从 BLOCKED 转换到 RUNNABLE 状态

2. RUNNABLE 与 WAITING 的状态转换

第一种场景,获得 synchronized 隐式锁的线程,调用无参数的 Object.wait() 方法。
第二种场景,调用无参数的 Thread.join() 方法。
当调用 A.join() 的时候,执行这条语句的线程会等待 thread A 执行完,而等待中的这个线程,其状态会从 RUNNABLE 转换到 WAITING。当线程 thread A 执行完,原来等待它的线程又会从 WAITING 状态转换到 RUNNABLE。
第三种场景,调用 LockSupport.park() 方法。

3. RUNNABLE 与 TIMED_WAITING 的状态转换

1.调用带超时参数的 Thread.sleep(long millis) 方法;
2.获得 synchronized 隐式锁的线程,调用带超时参数的 Object.wait(long timeout) 方法;
3.调用带超时参数的 Thread.join(long millis) 方法;
4.调用带超时参数的 LockSupport.parkNanos(Object blocker, long deadline) 方法;
5.调用带超时参数的 LockSupport.parkUntil(long deadline) 方法。

4.从 NEW 到 RUNNABLE 状态

调用线程对象的 start() 方法

5. 从 RUNNABLE 到 TERMINATED 状态

线程 的 interrupt() 方法。

进程与线程

进程

进程由来?

为了对并发执行的程序加以描述和控制,人们引入了“进程”的概念。

进程的定义

进程
是进程实体的运行过程,是系统进行资源分配调度的一个独立单位

属性

  • 进程是一个可拥有资源的独立单位;
  • 进程同时是一个可独立调度和分派的基本单位

系统是通过什么来控制进程的?

系统总是通过 PCB 对进程进行控制的。

进程控制块 PCB(Process Control Block)它是进程实体的一部分,是操作系统中最重要的记录型数据结构。

进程的三种基本状态:

  1. 就绪(Ready)状态
  2. 执行状态
  3. 阻塞状态

线程

线程由来?

再引入线程,则是为了减少程序在并发执行时所付出的时空开销。

进程和线程的关系?

通常一个进程都拥有若干个线程,至少也有一个线程。

线程作为调度和分派的基本单位,而进程作为资源拥有的基本单位。

线程的属性

  • 线程中的实体基本上不拥有系统资源,能保证独立运行。
  • 独立调度和分派的基本单位
  • 并发执行
  • 共享进程

线程上下文切换

cpu通过时间片分配算法来循环执行任务。时间片就是cpu为每个线程执行的时间。这个时间片执行时间很短,通常只有几十毫秒。所以cpu不停的切换,让我们以为线程是同时执行的。

但是线程切换的时候,会保存线程的状态,以便于下次切换回来的时候去加载这些状态。所以线程从保存到加载状态的过程,叫做一次上下文的切换。
上下文切换的弊端就是影响线程的执行速度。

线程实现

  • 内核线程:
  • 用户线程:用户程序中实现的线程

jdk1.2之前使用的是用户线程,1.2后使用的是内核线程
从操作系统的角度,可以简单认为,线程是系统调度的最小单元,一个进程可以包含多个线程。

状态和方法之间的对应图:

新启线程的方式

1.继承Thread
2.实现Runnable()
3.实现Callable,允许有返回值

public class ThreadDemo {public static void main(String[] args){UseCall useCall = new UseCall();FutureTask<String> futureTask = new FutureTask<>(useCall);new Thread(futureTask).start();}/*实现Callable接口,允许有返回值*/private static class UseCall implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("I am implements Callable");return "CallResult";}}
}

怎么样才能让Java里的线程安全停止工作?

  • 线程自然终止

    • 自然执行完
    • 抛出未处理异常
  • 通过API
    • stop(),resume(),suspend()已不建议使用,stop()会导致线程不会正确释放资源,suspend()容易导致死锁。
    • java线程是协作式,而非抢占式
      调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
    • isInterrupted() 判定当前线程是否处于中断状态。
    • static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false。
    • 方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。

Thread类中断线程

public class EndThread {private static class MyThread extends Thread{public MyThread(String name) {super(name);}@Overridepublic void run() {String threadName = Thread.currentThread().getName();while(!isInterrupted()) {System.out.println(threadName+" is run!");}System.out.println(threadName+" interrput flag is "+isInterrupted());}}public static void main(String[] args) throws InterruptedException {Thread endThread = new MyThread("myThread");endThread.start();Thread.sleep(20);endThread.interrupt();}}

中断Runnable类型的线程

public class EndRunnable {private static class myRunnable implements Runnable{@Overridepublic void run() {String threadName = Thread.currentThread().getName();while(!Thread.currentThread().isInterrupted()) {System.out.println(threadName+" is run!");}System.out.println(threadName+" interrput flag is "+Thread.currentThread().isInterrupted());}         }public static void main(String[] args) throws InterruptedException {myRunnable useRunnable = new myRunnable();Thread endThread = new Thread(useRunnable,"myThread");endThread.start();Thread.sleep(20);endThread.interrupt();}}

抛出InterruptedException异常的时候,要注意中断标志位

方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。

public class HasInterrputException {private static SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss_SSS");private static class UseThread extends Thread{public UseThread(String name) {super(name);}@Overridepublic void run() {String threadName = Thread.currentThread().getName();while(!isInterrupted()) {try {System.out.println("UseThread:"+formater.format(new Date()));Thread.sleep(100);} catch (InterruptedException e) {System.out.println(threadName+" catch interrput flag is "+isInterrupted()+ " at "+(formater.format(new Date())));interrupt();e.printStackTrace();}System.out.println(threadName);               }System.out.println(threadName+" interrput flag is "+isInterrupted());}}public static void main(String[] args) throws InterruptedException {Thread endThread = new UseThread("HasInterrputEx");endThread.start();System.out.println("Main:"+formater.format(new Date()));Thread.sleep(800);System.out.println("Main begin interrupt thread:"+formater.format(new Date()));endThread.interrupt();}}

守护线程(Daemon Thread)

应用中需要一个长期驻留的服务程序,但是不希望其影响应用退出,就可以将其设置为守护线程,如果JVM发现只有守护线程存在时,将结束进程。

总结就是守护线程与主线程共死。
注意,必须在线程启动之前设置(在start之前
而且守护线程的finally不能保证一定执行

       Thread thread= new Thread();//必须在start之前设置thread.setDaemon(true);thread.start();

ThreadLocal

ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

使用步骤:
创建一个与特定线程绑定的integer值:

ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();

如果我们想拿到值或者设置值,我们只需要调用get()或set()方法

threadLocalValue.set(1);
threadLocalValue.get();

ThreadLocal初始化使用withInitia()方法

ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

移除方法:

threadLocal.remove();

具体使用:


public class UseThreadLocal {static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){@Overrideprotected Integer initialValue() {return 100;}};/*** 运行3个线程*/public void StartThreadArray(){Thread[] runs = new Thread[3];for(int i=0;i<runs.length;i++){runs[i]=new Thread(new TestThread(i));}for(int i=0;i<runs.length;i++){runs[i].start();}}/***类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响*/public static class TestThread implements Runnable {int id;public TestThread(int id){this.id = id;}public void run() {System.out.println(Thread.currentThread().getName()+":start");Integer s = threadLocal.get();//获得变量的值s = s+id;threadLocal.set(s);System.out.println(Thread.currentThread().getName()+":"+ threadLocal.get());//threadLocal.remove();}}public static void main(String[] args){UseThreadLocal test = new UseThreadLocal();test.StartThreadArray();}
}

结果:

Thread-0:start
Thread-1:start
Thread-0:100
Thread-2:start
Thread-1:101
Thread-2:102
注意:

它的实现结构,数据存储于线程相关的ThreadLocalMap,其内部条目是弱引用。
通常弱引用都会和引用队列配合清理机制使用,但是ThreadLocal是个例外,它并没有这么做。
这意味着,废弃项目的回收依赖于显式地触发,否则就要等待线程结束,进而回收相应ThreadLocalMap!这就是很多OOM的来源,所以通常都会建议,应用一定要自己负
责remove,并且不要和线程池配合,因为worker线程往往是不会退出的

第17讲 一个线程两次调用sart()方法会出现什么情况相关推荐

  1. 一个线程两次调用sart()方法会出现什么情况?

    一个线程两次调用sart()方法会出现什么情况? 今天我们来深入聊聊线程,相信大家对于线程这个概念都不陌生,它是Java并发的基础元素,理解.操纵.诊断线程是Java工程师的必修课,但是你真的掌握线程 ...

  2. Java经典面试题:一个线程两次调用start()方法会出现什么情况?

    大家好,我是 Oracle首席工程师杨晓峰. 今天想和大家深入聊聊线程,相信大家对于线程这个概念都不陌生,它是Java并发的基础元素,理解.操纵.诊断线程是Java工程师的必修课,但是你真的掌握线程了 ...

  3. 关于同一线程两次调用EnterCriticalSection的测试

    #include "stdafx.h" #include <iostream> using namespace std; #include <windows.h& ...

  4. Java基础编程题目——定义一个比较两个数大小的方法

    编写一个方法判断两个数的大小,并返回较大的值 import java.util.Scanner;public class Max {public static void main(String[] a ...

  5. 请问当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

    不能.其它线程只能访问该对象的非同步方法,同步方法则不能进入.因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的 ...

  6. java线程基础巩固---线程生命周期以及start方法源码剖析

    上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...

  7. JAVA中初始化线程的两种方法_java中最简单的方式新起一个线程

    启动一个线程 在一个方法中启动一个线程,有两种方法 第一种是让类实现Runable接口,这样的话编译器就会提示你实现里面的未实现的方法(就是run方法) 第二种是,现在方法中new一个线程,然后直接调 ...

  8. java new一个线程执行完后会自动销毁吗_Java基础总结,超级全的面试题

    1. static关键字是什么意思?Java 中是否可以覆盖(override)一个 private 或者是 static 的方法?是否可以在 static 环境中访问非static 变量? stat ...

  9. java 停止一个线程_Java如何停止一个线程

    线程正常执行完毕,正常结束. 2.监视某些条件,直到某些条件成立,结束线程. class TestMyThread extends Thread { private volatile boolean ...

最新文章

  1. 30分钟学会mysql_30分钟回顾MySQL语法(下)
  2. 复盘二进制的习题(1)
  3. Unity应用架构设计(10)——绕不开的协程和多线程(Part 1)
  4. 实习面试问题整理(一)
  5. ES6新特性_变量的解构赋值---JavaScript_ECMAScript_ES6-ES11新特性工作笔记006
  6. android实现应用程序仅仅有在第一次启动时显示引导界面
  7. mysql 备份恢复 asp_ASP中怎么实现SQL数据库备份、恢复!
  8. 电子邮件如何追溯到他们的源IP地址
  9. 机器人动力学——拉格朗日法
  10. 好学易懂 从零开始的插头DP(一)
  11. 词汇学习系列(一):252个基本词根详解
  12. mysql rds 是什么_mysql.rds.aliyuncs.com
  13. The Apache Tomcat Native library which allows optimal performance in production environments wasn
  14. 大象知道“故事怎么讲”
  15. python 基于Tkinter的姻缘测试器
  16. 解决IE文件无法正常下载,其他浏览器可以正常下载
  17. 基于腾讯云的 Rust 和 WebAssembly 函数即服务
  18. Theano官方文档的测试和总结(1):安装、基础语法、逻辑斯蒂回归
  19. 决定考BEC商务英语
  20. 西安翻译学院东区计算机教室,西安翻译学院:200间教室跨进智慧时代

热门文章

  1. 浅谈Redis的隐性成本
  2. linux中键盘按键键值修改
  3. 测试-http接口测试点
  4. Excel表格里面做省份,市(二级联动效果)
  5. oracle 查询磁盘读写,监控磁盘读写状况
  6. FPGA/IC秋招经典100题(含详解)
  7. 解决Visual Studio的IIS与无法启动调试器问题
  8. 时间序列分析基础(定义、均值、方差、自协方差及相关性)
  9. springboot安全组件总结
  10. 重审新消费品牌的长远发展