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. 用Taro写一个微信小程序(三)—— 配置dva

    一.关于dva dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻 ...

  2. 从零开始学架构(三)UML建模

    文章大纲 1.  文章介绍 2.  UML概述 3.  静态模型 4.  动态模型 5.  UML建模的一般过程 一.文章介绍 1.1为什么学习UML (1)UML是一种软件架构的模型表现方法,用于项 ...

  3. Perm 排列计数

    题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很 ...

  4. 约会Rendezvous

    约会 Rendezvous 内存限制:128 MiB 时间限制:1000 ms 标准输入输出     题目描述 给定一个有 nnn 个顶点的有向图,每个顶点有且仅有一条出边.每次询问给出两个顶点 ai ...

  5. npm ERR! Unexpected end of JSON input while parsing near '...'解决方法

    npm install时出现npm err! Unexpected end of JSON input while parsing near'...'错误 输入  npm cache clean -- ...

  6. react中 props,state,render函数的关系

    1.当组件的 state 或者 props 发生改变的时候,自己的render函数就会重新执行. 2. 当父组件的render函数执行时,其所有子组件的render函数都会重新执行.

  7. excel VBA构造函数就是这么简单

    Function test(a As Integer)'构造函数名字为test参数为a且为int型  If a >= 90 Then     Debug.Print "优秀" ...

  8. 一、RabbitMQ 概念详解和应用

    消息队列和同步请求的区别 无论RabbitMQ还是Kafka,本质上都是提供了基于message或事件驱动异步处理业务的能力,相比于http和rpc的直接调用,它有着不可替代的优势: 1. 解耦,解耦 ...

  9. Qt 新手实战项目之手把手打造一个串口助手

    一前景 很多时候我们在学习一门新的语言,一直在学习各种语法和记住各种关键字,很容易产生枯燥的情绪,感觉学习这些玩意儿不知道用在什么地方,心里很是苦恼,这不,我在这记录下我学习Qt的第一个的小项目-串口 ...

  10. 7、解决windows10家庭版无法远程连接服务器的问题

    (1)方法一: 升级windows10为专业版,因为win10家庭版没有组策略: (2)方法二:通过远程命令: 同时按住"win+r"键调出"运行",在方框内输 ...