Java多线程(上)
Java多线程
程序、进程和线程
一、程序
- 程序是存储在磁盘上, 包含可执行机器指令和数据的静态实体。 即进程或者任务是处于活动状态的计算机程序。
二、进程
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例,即运行中的程序。
一个运行着的程序,可能有多个进程。进程在操作系统中执行特定的任务。
程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
三、 线程
- 线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。
- 线程,轻量级,不拥有自己独立的内存资源,共享进程的代码区、数据区、堆区(注意没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、当前目录、用户ID和组ID等资源。
- 线程拥有自己独立的栈,因此也有自己独立的局部变量。
- 一个进程可以同时拥有多个线程,即同时被系统调度的多条执行路线,但至少要有一个主线程。
线程实现
继承Thread类
线程是程序中执行的线程。Java虚拟机允许应用程序同时运行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于具有较低优先级的线程执行。 每个线程可能也可能不会被标记为守护进程。 当在某个线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。当Java虚拟机启动时,通常会有一个非守护进程线程(通常调用某个指定类的名为
main
的方法)。 Java虚拟机继续执行线程,直到发生以下任一情况:
- 已调用类
Runtime
的exit
方法,并且安全管理器已允许执行退出操作。- 通过调用
run
方法返回或抛出超出run
方法传播的异常,所有非守护程序线程的线程都已死亡。
创建线程方式一:继承Thread类,重写run()方法,调用start()开启线程
public class MyThread extends Thread{
@Override
public void run() {
//子线程方法
for (int i = 0; i < 20; i++) {
System.out.println("我是子线程" + i);
}
}
public static void main(String[] args) {
//开启子线程
new MyThread().start();
for (int i = 0; i < 20; i++) {
System.out.println("我是主线程" + i);
}
}
}
执行结果
可以发现,主线程和子线程是”同时“进行的。
Thread类实现了Runnable接口,内部通过静态代理调用了run()方法
实现Runnable接口
public class MyThread implements Runnable{
@Override
public void run() {
//子线程方法
for (int i = 0; i < 20; i++) {
System.out.println("我是子线程" + i);
}
}
public static void main(String[] args) {
//创建线程对象,代理线程
new Thread(new MyThread()).start();
for (int i = 0; i < 20; i++) {
System.out.println("我是主线程" + i);
}
}
}
使用方法基本和继承Thread类相同,执行结果也相似。
实现Callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行: Future<Boolean> result1 = ser. submit(t1);
- 获取结果: boolean r1 = result1.get()
- 关闭服务: ser. shutdownNow();
示例代码
public class MyThread implements Callable<Boolean> {
@Override
public Boolean call() {
//子线程方法
System.out.println("执行了子线程");
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread t1 = new MyThread();
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> result1 = ser.submit(t1);
//获取结果
boolean r1 = result1.get();
//关闭服务
ser. shutdownNow();
}
}
线程状态
线程五大状态
- 创建状态
- 就绪状态
- 阻塞状态
- 运行状态
- 死亡状态
五个状态的转化
创建状态:
Thread t = new Thread()
线程对象一旦创建就进入到了新生状态。
就绪状态:
当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行。
运行状态:
进入运行状态,线程才真正执行线程体的代码块。
阻塞状态:
当调用sleep, wait 或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待cpu调度执行。
不一定每个线程都会进入阻塞状态
死亡状态:
线程中断或者结束,一旦进入死亡状态,就不能再次启动。
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程,别用这个方式 |
boolean isAlive() | 测试线程是否处于活动状态 |
线程停止
- 开始线程可以使用start()方法。
- 建议线程正常停止,利用次数,不建议死循环。
- 建议使用标志位,设置一个标志位。
- 不要使用stop或者destroy等过时或者JDK不建议使用的方法。
使用标志位的例子
public class MyThread implements Runnable{
private boolean flag = true;
@Override
public void run() {
//子线程方法
int i = 0;
while (flag) {
System.out.println("我是子线程" + i++);
}
}
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
MyThread thread = new MyThread();
new Thread(thread).start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程" + i);
if (i == 800) {
thread.stop();
System.out.println("线程停止了");
}
}
}
}
结果如图
线程休眠
- sleep (时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时
- 每一个对象都有一-个锁, sleep不会释放锁
线程休眠的例子:
public class MyThread{
public static void main(String[] args) throws InterruptedException {
while (true) {
Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
Thread.sleep(1000);
}
}
}
每一秒输出当前时间,使用sleep(1000)来使得每次执行完休眠一秒。
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不-定成功!看CPU心情
示例
public class MyThread{
public static void main(String[] args) {
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName() + "线程开始");
Thread.yield(); //线程礼让
System.out.println(Thread.currentThread().getName() + "线程结束");
};
new Thread(runnable, "a").start();
new Thread(runnable, "b").start();
new Thread(runnable, "c").start();
}
}
执行结果
可以发现每次的结果都不相同
线程联合
可以想象成一个VIP线程,强制插队,查到当前线程,直到自己执行完毕。非常霸道
会使得线程阻塞。
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
for (int i = 0; i < 10; i++) {
System.out.println("VIP线程来了" + i);
}
};
Thread thread = new Thread(runnable);
thread.start();
for (int i = 0; i < 50; i++) {
if (i == 30) thread.join();
System.out.println("主线程" + i);
}
}
}
结果如图
线程状态观测
线程状态。线程可以处于以下状态之一:
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在Java虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。 - TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。
- NEW
通过以下代码演示线程状态的监听
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {}
}
});
System.out.println(thread.getState());
thread.start();
while (thread.getState() != Thread.State.TERMINATED) {
Thread.sleep(500);
System.out.println(thread.getState());
}
}
}
运行结果
线程的优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程, 线程调度
器按照优先级决定应该调度哪个线程来执行。
观察Thread类源代码,可以看到优先级在jdk中的定义
/**
* The minimum priority that a thread can have.
*/
public static final int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public static final int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public static final int MAX_PRIORITY = 10;
- 线程的优先级用数字表示,范围从1~10.
- Thread.MIN_ PRIORITY= 1;
- Thread.MAX_ PRIORITY= 10;
- Thread.NORM_ PRIORITY = 5;
我们可以通过
System.out.println(Thread.currentThread().getPriority());
来输出当前线程的优先级,可以得到默认优先级是5。
可以使用以下方式改变或获取优先级
getPriority() //得到优先级
setPriority(int xxx) 设置优先级
如果设置的优先级超出限制,则会抛出一个IllegalArgumentException异常:
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度。
最好在start前设置优先级
守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存垃圾回收等待..
所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
演示,先定义两个线程,一个守护线程,一个普通线程
Thread daemon = new Thread(()->{
while (true) {
System.out.println("守护线程...");
}
});
Thread normal = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("子线程...");
}
System.out.println("子线程结束");
});
然后设置守护线程
daemon.setDaemon(true);
启动两个线程后,可以看到,守护线程并没有结束就停止了程序。
Java多线程(下)https://www.cnblogs.com/chaofanq/p/15055853.html
Java多线程(上)的更多相关文章
- Java 多线程(上)
启动一个多线程 多线程即在同一时间,可以做多件事情,创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类 线程概念 首先要理解进程(Processor)和线程(Thread)的区别 ...
- java从基础知识(十)java多线程(上)
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点 ...
- 50个Java多线程面试题(上)
Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者精通多线程技术并且有丰富的 Java 程序开发.调试.优化经验 ...
- 新鲜出炉!面试90%会被问到的Java多线程面试题,史上最全系列!
前言 最近很多粉丝朋友私聊我说能不能给整理出一份多线程面试题出来,说自己在最近的面试中老是被问到这一块的问题被问的很烦躁,前一段时间比较忙没时间回私信,前两天看到私信我也是赶紧花了两天给大家整理出这一 ...
- 40个Java多线程问题总结
前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...
- Java多线程基础知识篇
这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程 2 线程的生命周期和状态控制
一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...
随机推荐
- 利用UltraScale和UltraScale+FPGA和MPSOC加速DSP设计生产力
利用UltraScale和UltraScale+FPGA和MPSOC加速DSP设计生产力 Accelerating DSP Design Productivity with UltraScale an ...
- 嵌入式Linux设备驱动程序:发现硬件配置
嵌入式Linux设备驱动程序:发现硬件配置 Embedded Linux device drivers: Discovering the hardware configuration Interfac ...
- 深入 Go 中各个高性能 JSON 解析库
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/535 其实本来我是没打算去看 JSON 库的性能问题的,但是最近我对 ...
- javascript中的设计模式
什么是设计模式 设计模式(Design Pattern)是一套被反复使用.多数人知晓的.经过分类的.代码设计经验的总结. 使用设计模式的目的:为了代码可重用性.让代码更容易被他人理解.保证代码可靠性. ...
- React开发中react-route-dom使用BrowserRouter部署到服务器上刷新时报404的问题
React项目部署中遇到的问题 react开发中react-route使用BrowserRoute路径在iis服务器上刷新时报404的问题 解决:在发布的项目根目录添加web.config配置文件 在 ...
- Django-ORM-事务和锁
一.锁 行级锁 返回一个锁住行,值到事物结束的查询集. 所有匹配的行将被锁住,直到事物结束,这就意味着可以通过锁防止数据被其他事务修改. 一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞直到锁被 ...
- 根据所处位置提取单元格内容的函数(left、right、mid)和查找字符串位于单元格内容第几位的函数(find)
1.从左到右提取:left(value,num_chars) 注释:value为操纵单元格,num_chars表示截取的字符的数量 2.从右往左提取:right(value,num_chars) 注释 ...
- pip安装setuptools_rust报错
公司项目中有主备CDN存在,由于阿里云以及腾讯云的预热功能不支持自动(一般是云函数),所以就根据云厂商给的脚本稍作更改,手动传入数据来进行预热. 由于之前部署在centos7.7系统python2.7 ...
- 103、kickstart自动化安装操作系统
103.1.前言: 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装. 常规的办法有: 光盘安装系统===>一个 ...
- 22、oracle子查询
22.1.什么是子查询: 1.子查询就是在一条sql语句中嵌入select语句: 2.子查询可区分为关联子查询和非关联子查询,他们和主查询之间的执行顺序和关系是不同的: 22.2.关联子查询: 1.说 ...