Java并发编程(01):线程的创建方式,状态周期管理
本文源码:GitHub·点这里 || GitEE·点这里
一、并发编程简介
1、基础概念
- 程序
与计算机系统操作有关的计算机程序、规程、规则,以及可能有的文件、文档及数据。
- 进程
进程是计算机中的程序,关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 线程
线程是操作系统能够进行运算调度的最小单位,包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
- 顺序编程
程序中的所有步骤在任意时刻只能执行一个步骤。编程中绝大部分场景都是基于顺序编程。
- 并发编程
在一台处理器上“同时”处理多个任务,并行处理程序中的复杂耗时任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。
2、入门案例
public class HelloThread {
public static void main(String[] args) {
System.out.println("Hello,Thread");
// 当前线程名称
System.out.println(Thread.currentThread().getName());
// 线程系统的管理接口
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.getAllThreadIds() ;
for (long id : threadIds) {
ThreadInfo threadInfo = threadMXBean.getThreadInfo(id) ;
System.out.println(threadInfo.getThreadId()+
":"+threadInfo.getThreadName());
}
}
}
打印结果:
5:Monitor Ctrl-Break
4:Signal Dispatcher
3:Finalizer
2:Reference Handler
1:main
由此可知上述一段简单的Java程序,不止一条main线程在执行。
二、线程创建方式
1、继承Thread类
Thread类的基础结构:
class Thread implements Runnable
这里已经实现了Runnable接口。
public class CreateThread01 {
public static void main(String[] args) {
// 调用方法
MyThread1 myThread1 = new MyThread1() ;
myThread1.start();
}
}
class MyThread1 extends Thread {
// 设置线程名称
public MyThread1 (){
super("CicadaThread");
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
2、实现Runnable接口
如果创建的线程类已经存在父类,则不能再继承Thread类,在Java中不允许多继承,这时就可以实现Runnable接口。
public class CreateThread02 {
public static void main(String[] args) {
Thread thread = new Thread(new MyThread2(),"MyThread2") ;
thread.start();
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" run ...");
}
}
3、匿名内部类
在一个类里面定义一个类,称为内部类。内部类就相当于外部类的一个成员,可以把内部类看成一个整体。
public class CreateThread03 {
public static void main(String[] args) {
//方式1
new Thread("ThreadName1") {
public void run() {
System.out.println("1:"+Thread.currentThread().getName());
};
}.start();
//方式2
new Thread(new Runnable() {
public void run() {
System.out.println("2:"+Thread.currentThread().getName());
}
},"ThreadName2"){
// 这里重写了run方法
@Override
public void run() {
System.out.println("3:"+Thread.currentThread().getName());
}
}.start();
}
}
4、返回值线程
顾名思义,该线程线程异步执行后,可以返回线程的处理结果。
public class CreateThread04 {
public static void main(String[] args) throws Exception {
MyThread4 myThread4 = new MyThread4();
FutureTask<Integer> task = new FutureTask<>(myThread4);
Thread thread = new Thread(task,"TaskThread");
thread.start();
// 等待获取结果
// Integer result = task.get();
// 设置获取结果的等待时间,超时抛出:TimeoutException
Integer result = task.get(3, TimeUnit.SECONDS) ;
System.out.println("result="+result);
}
}
class MyThread4 implements Callable<Integer> {
// 封装线程执行的任务
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
return 2+3;
}
}
5、定时任务
Timer是后台线程执行任务调度的工具类,可以根据规则配置定期执行或者重复执行。
class TimerTask implements Runnable
任务类:TimerTask结构实现Runnable接口。
public class CreateThread05 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("延迟1s,每隔3s执行一次");
}
}, 1000, 3000);
}
}
6、线程池管理
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
public class CreateThread06 {
public static void main(String[] args) {
Executor threadPool = Executors.newFixedThreadPool(5);
for(int i = 0 ;i < 5 ; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
}
}
三、线程状态管理
1、状态描述
- NEW
初始状态:构建线程实例后,调用start()方法启动前,处于该状态。
- RUNNABLE
运行状态:在Java线程中,就绪和运行两个状态称作运行状态,在实际的执行过程中,这两个状态是随时可能切换的。启动start()方法被调用,或者sleep()后,join()结束等,就进入RUNNABLE就绪状态,开始等待CPU时间片;线程调度选中该线程、并分配了CPU时间片后,该线程尽管处于Runnable状态,就是运行状态(Running);
- BLOCKED
阻塞状态:通常指被锁机制阻塞,表示线程正在获取有锁控制的资源。
- WAITING
等待状态:进入该状态的线程,等待被其他线程发出通知或中断,也称显式唤醒。
- TIMED_WAITING
超时等待状态:该状态不同于WAITING状态,该状态的线程可以在指定的时间后自动唤醒;
- TERMINATED
终止状态:表示当前线程任务执行完毕。
2、案例流程分析
public class StateCycle01 {
public static void main(String[] args) throws Exception {
// 进入初始状态
StateThread01 stateThread01 = new StateThread01();
FutureTask<String> task = new FutureTask<>(stateThread01);
Thread thread = new Thread(task,"GetValueThread");
// 运行状态
thread.start();
// 超时等待结果
String result = task.get(3, TimeUnit.SECONDS) ;
System.out.println("result="+result);
StateThread02 stateThread02 = new StateThread02() ;
Thread thread1 = new Thread(stateThread02,"WaitThread");
thread1.start();
}
}
class StateThread01 implements Callable<String> {
@Override
public String call() throws Exception {
// 超时等待
Thread.sleep(1000);
return "Hello,Cicada";
}
}
class StateThread02 implements Runnable {
@Override
public void run() {
synchronized (StateCycle01.class) {
System.out.println("进入线程...");
try {
// 等待状态,放弃对象锁
StateCycle01.class.wait(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程继续...");
}
}
}
上述流程描述了线程不同状态之间的切换,基本流程图如下。
线程的状态描述起来不算复杂,但是每个状态间的切换,是非常的复杂,后续会分模块单个解释。
四、优缺点总结
1、优点说明
最直接作用使程序执行的效率大幅度提升;程序异步解耦,在web开发中,经常有后续的程序要执行,有需要快速的用户界面响应;当然熟练使用并发编程,也是一个优秀程序员必备技能 。
2、缺点分析
并发编程学习的曲线非常陡峭,难度较大;多线程之间争抢资源容易出现问题;并不是线程越多,执行速度就越快,线程之前切换是耗时的,需要合理创建和使用锁机制;线程创建和之间的通信需要很清晰的逻辑;线程死锁问题更是无法完全避免的问题;所以在一般情况下公司对线程使用的规范是十分严格的。
五、源代码地址
GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent
Java并发编程(01):线程的创建方式,状态周期管理的更多相关文章
- Java并发编程:线程的创建
Java并发编程:线程的创建 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} J ...
- java并发编程基础——线程的创建
一.基础概念 1.进程和线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java并发编程:线程池的使用(转载)
转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- Java并发编程:线程池的使用(转载)
文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
- [转]Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
随机推荐
- 解决 WinForm 重写 CreateParams 隐藏窗口以后的显示问题
WinForm 启动时隐藏窗体最简单有效的办法是重写 CreateParams protected override CreateParams CreateParams { get { base.Vi ...
- vue项目打包,生成dist文件夹,如何修改文件夹的名字
vue项目打包之后的dist文件目录是如下 如果想要修改dist或则static文件名称,需要打开config / index.js build: { // Template for index.ht ...
- Oracle不同版本中序列的注意点
<span style="font-size:14px;">create table manager ( userid NUMBER(10), username VAR ...
- dubbo同步/异步调用的方式
我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制:基于这种机制,Dubbo 实现了以下几种调用方式: 同步调用(默认) 异步调用 参数回调 事件通知 同步 ...
- VMware 安装 Centos7 后,没有ipv4的地址,或者地址显示127.0.0.1
问题描述:VMware 安装 Centos7 后,没有ipv4的地址,或者地址显示127.0.0.1 安装环境:VMware版本:VMware-workstation-full-14.0.0.2405 ...
- java 计算一个月有多少天和多少周
import java.util.Calendar; /** * 功能概述:计算指定年月的天数和周数<br> */ public class Test{ public static voi ...
- 一:MYsql登录,服务开启和停止
字段的属性: 1:名称 2:数据类型 3:长度 4:约束 SQL的分类:(结构化查询语言) 1:数据查询语言DQL select 2:数据操纵语言DML insert delete u ...
- SpringCloud快速搭建
1.SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.负载均衡.微代理.事件总线.全局锁.决策竞选.分布式会话等等.它运行环境简单,可以在开发 ...
- linux有些sh文件,为什么要用 ./ 来执行
因为有环境变量PATH,里面包含了许多目录,这些目录下的可执行文件就无需输入完整路径来执行.你可以用 echo "$PATH"查看当前的环境变量包含的目录,自带的命令文件都是在PA ...
- 吴裕雄--天生自然python学习笔记:Python3 File(文件) 方法
open() 方法 Python open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError. 注意:使用 open() ...