第17讲 一个线程两次调用sart()方法会出现什么情况
一个线程两次调用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)它是进程实体的一部分,是操作系统中最重要的记录型数据结构。
进程的三种基本状态:
- 就绪(Ready)状态
- 执行状态
- 阻塞状态
线程
线程由来?
再引入线程,则是为了减少程序在并发执行时所付出的时空开销。
进程和线程的关系?
通常一个进程都拥有若干个线程,至少也有一个线程。
线程作为调度和分派的基本单位,而进程作为资源拥有的基本单位。
线程的属性
- 线程中的实体基本上不拥有系统资源,能保证独立运行。
- 独立调度和分派的基本单位
- 并发执行
- 共享进程
线程上下文切换
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()方法会出现什么情况相关推荐
- 一个线程两次调用sart()方法会出现什么情况?
一个线程两次调用sart()方法会出现什么情况? 今天我们来深入聊聊线程,相信大家对于线程这个概念都不陌生,它是Java并发的基础元素,理解.操纵.诊断线程是Java工程师的必修课,但是你真的掌握线程 ...
- Java经典面试题:一个线程两次调用start()方法会出现什么情况?
大家好,我是 Oracle首席工程师杨晓峰. 今天想和大家深入聊聊线程,相信大家对于线程这个概念都不陌生,它是Java并发的基础元素,理解.操纵.诊断线程是Java工程师的必修课,但是你真的掌握线程了 ...
- 关于同一线程两次调用EnterCriticalSection的测试
#include "stdafx.h" #include <iostream> using namespace std; #include <windows.h& ...
- Java基础编程题目——定义一个比较两个数大小的方法
编写一个方法判断两个数的大小,并返回较大的值 import java.util.Scanner;public class Max {public static void main(String[] a ...
- 请问当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
不能.其它线程只能访问该对象的非同步方法,同步方法则不能进入.因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的 ...
- java线程基础巩固---线程生命周期以及start方法源码剖析
上篇中介绍了如何启动一个线程,通过调用start()方法才能创建并使用新线程,并且这个start()是非阻塞的,调用之后立马就返回的,实际上它是线程生命周期环节中的一种,所以这里阐述一下线程的一个完整 ...
- JAVA中初始化线程的两种方法_java中最简单的方式新起一个线程
启动一个线程 在一个方法中启动一个线程,有两种方法 第一种是让类实现Runable接口,这样的话编译器就会提示你实现里面的未实现的方法(就是run方法) 第二种是,现在方法中new一个线程,然后直接调 ...
- java new一个线程执行完后会自动销毁吗_Java基础总结,超级全的面试题
1. static关键字是什么意思?Java 中是否可以覆盖(override)一个 private 或者是 static 的方法?是否可以在 static 环境中访问非static 变量? stat ...
- java 停止一个线程_Java如何停止一个线程
线程正常执行完毕,正常结束. 2.监视某些条件,直到某些条件成立,结束线程. class TestMyThread extends Thread { private volatile boolean ...
最新文章
- 30分钟学会mysql_30分钟回顾MySQL语法(下)
- 复盘二进制的习题(1)
- Unity应用架构设计(10)——绕不开的协程和多线程(Part 1)
- 实习面试问题整理(一)
- ES6新特性_变量的解构赋值---JavaScript_ECMAScript_ES6-ES11新特性工作笔记006
- android实现应用程序仅仅有在第一次启动时显示引导界面
- mysql 备份恢复 asp_ASP中怎么实现SQL数据库备份、恢复!
- 电子邮件如何追溯到他们的源IP地址
- 机器人动力学——拉格朗日法
- 好学易懂 从零开始的插头DP(一)
- 词汇学习系列(一):252个基本词根详解
- mysql rds 是什么_mysql.rds.aliyuncs.com
- The Apache Tomcat Native library which allows optimal performance in production environments wasn
- 大象知道“故事怎么讲”
- python 基于Tkinter的姻缘测试器
- 解决IE文件无法正常下载,其他浏览器可以正常下载
- 基于腾讯云的 Rust 和 WebAssembly 函数即服务
- Theano官方文档的测试和总结(1):安装、基础语法、逻辑斯蒂回归
- 决定考BEC商务英语
- 西安翻译学院东区计算机教室,西安翻译学院:200间教室跨进智慧时代