Java多线程

程序、进程和线程

一、程序

  • 程序是存储在磁盘上, 包含可执行机器指令和数据的静态实体。 即进程或者任务是处于活动状态的计算机程序。

二、进程

  • 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例,即运行中的程序。

  • 一个运行着的程序,可能有多个进程。进程在操作系统中执行特定的任务。

  • 程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。

三、 线程

  • 线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。
  • 线程,轻量级,不拥有自己独立的内存资源,共享进程的代码区、数据区、堆区(注意没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、当前目录、用户ID和组ID等资源。
  • 线程拥有自己独立的栈,因此也有自己独立的局部变量。
  • 一个进程可以同时拥有多个线程,即同时被系统调度的多条执行路线,但至少要有一个主线程。

线程实现

继承Thread类

线程是程序中执行的线程。Java虚拟机允许应用程序同时运行多个执行线程。

每个线程都有优先权。 具有较高优先级的线程优先于具有较低优先级的线程执行。 每个线程可能也可能不会被标记为守护进程。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。

当Java虚拟机启动时,通常会有一个非守护进程线程(通常调用某个指定类的名为main的方法)。 Java虚拟机继续执行线程,直到发生以下任一情况:

  • 已调用类Runtimeexit方法,并且安全管理器已允许执行退出操作。
  • 通过调用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

      已退出的线程处于此状态。

    线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。

通过以下代码演示线程状态的监听

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多线程(上)的更多相关文章

  1. Java 多线程(上)

    启动一个多线程 多线程即在同一时间,可以做多件事情,创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类 线程概念 首先要理解进程(Processor)和线程(Thread)的区别 ...

  2. java从基础知识(十)java多线程(上)

    线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元.另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点 ...

  3. 50个Java多线程面试题(上)

    Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者精通多线程技术并且有丰富的 Java 程序开发.调试.优化经验 ...

  4. 新鲜出炉!面试90%会被问到的Java多线程面试题,史上最全系列!

    前言 最近很多粉丝朋友私聊我说能不能给整理出一份多线程面试题出来,说自己在最近的面试中老是被问到这一块的问题被问的很烦躁,前一段时间比较忙没时间回私信,前两天看到私信我也是赶紧花了两天给大家整理出这一 ...

  5. 40个Java多线程问题总结

    前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...

  6. Java多线程基础知识篇

    这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...

  7. Java多线程系列--“JUC锁”03之 公平锁(一)

    概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...

  8. Java多线程系列--“JUC锁”04之 公平锁(二)

    概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...

  9. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

随机推荐

  1. CodeGen处理Synergy方法目录

    CodeGen处理Synergy方法目录 如果Synergy应用程序开发环境包括使用Synergy/DE xfServerPlus,则可以基于Synergy方法目录中包含的元数据生成代码.要启用此功能 ...

  2. 用CLion实现本地方法并给java调用

    众所周知,PHP是世界上最好的语言,java排第二,因为PHP无所不能.但是在某些场景下java还要调用本地方法来提高执行的效率,故java只能排第二.java提供了jni(Java Native I ...

  3. java面试必知必会——排序

    二.排序 时间复杂度分析 排序算法 平均时间复杂度 最好 最坏 空间复杂度 稳定性 冒泡 O(n²) O(n) O(n²) O(1) 稳定 选择 O(n²) O(n²) O(n²) O(1) 不稳定 ...

  4. 【NX二次开发】获取尺寸信息UF_DRF_ask_draft_aid_text_info

    获取尺寸信息UF_DRF_ask_draft_aid_text_info 例如获取下面这三个的尺寸信息. 图1 输出结果: 10 <T+0.1!-0.1> 图2 输出结果: 10 < ...

  5. 合宙Luat直播间即将开启,你揭开行业奥秘,让你快人一步。

    嗨~刚陪你们过儿童节 和你们一起成长的合宙Luat 又有新计划 -- 合宙Luat官方直播即将开启 - 敬请关注 - - 官方直播什么内容 - 可能是合宙研发动态 可能是新品发布资讯 可能是行业大咖分 ...

  6. 数据泵导出报错ORA-31693 ORA-02354 ORA-01466

    1.Oracle数据泵导出schema时有报错: Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - P ...

  7. Springboot集成Spring Security实现JWT认证

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 Spring Security作为成熟且强大的安全框架,得到许多大厂的青睐.而作为前后端分离的SSO方案,JWT ...

  8. js中有对象的key怎么获取对应的值

    一般人的思路是这样的 var obj = {"name1":"张三","name2":"李四"}; var key = ...

  9. excel VBA一个fuction同时执行多个正则表达式,实现方法

    代码: Function zhengze3(ze1 As String, ze2 As String, Rng1 As Range, Rng2 As Range)    Set regx1 = Cre ...

  10. excel VBA返回选中单元格区域的行数、列数,以及活动单元格的行号和列号

    Private Sub Worksheet_SelectionChange(ByVal Target As Range) '可以直接sub(),不然选择就会触发vba    Dim rows_coun ...