java线程

简介: 并发与并行3.1创建和运行线程多线程创建方式一、二、使用lambda表达式写法更精简Thread 与Runnable关系,看下源码,Runable里走的也是走的run方法方法:把线程和任务合并起来了,方法二,把线程和任务分开来了。Runable更容易与线程池等高级api配合使用。

并发与并行

3.1创建和运行线程

多线程创建方式

一、

二、

使用lambda表达式写法更精简

Thread 与Runnable关系,看下源码,Runable里走的也是走的run方法

方法:把线程和任务合并起来了,方法二,把线程和任务分开来了。Runable更容易与线程池等高级api配合使用。

三、

线程运行的原理

1、线程运行原理-debug法

2、线程运行原理-栈帧图解法

Java Virtual Machine Stacks (Java 虚拟机栈)
我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟
机就会为其分配一块栈内存。
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
image.png

首先执行类加载,把类的二进制的字节码加载到jvm虚拟机中,加载的位置就是把这些字节码文件放到一个方法区中的内存。类加载完成后,虚拟机就会启动一个main的主线程,并且给这个主线程分配一块线程栈内存,接下来线程就交给了操作系统的任务调度器去调度执行。cpu的时间片分给谁谁就执行谁,cpu的时间片分给了我们主线程,cpu核心就开始运行我们主线程的代码,栈内存中还有个程序计数器,它的作用就是告诉cpu要执行哪行代码了,每个栈帧包括,局部变量、返回地址、锁记录、操作数栈。要执行哪个方法了,就先准备出哪个方法栈帧内存,然后再调动执行。当方法调用完成后,这个栈帧内存就释放掉了。要运行main方法的时候,先创建一个main栈帧在栈内存中,这其中包括局部变量,args,他的地址指向是创建了一个new String[]对象,这个对象都存到堆内存中,它的这个返回地址没有,以为这个主方法里执行完 method1 就结束了。

3、线程运行原理-多线程

debug模式查看,注意断点选择Thread

什么是线程的上下文切换

操作系统的任务调度器,把时间片分配给每个线程运行的时候,每个线程的时间片总有用完的时候,cpu的使用权交给其他线程,当前线程就会发生一次上下文切换。就是线程从使用cpu到不使用cpu,这个就称之为一次上下文切换。

发生的场景:

线程的cpu时间片用完了

垃圾回收

有更高优先级的线程需要运行

以上三种是被动的

线程自己调用了sleep、yieid、wait、join、synchronized、lock等方法

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念
就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
Context Switch 频繁发生会影响性能。

常见方法
image.png
image.png

接下来挨个说下每个方法的用法:

1、start 和run

start 是启动线程

run是启动线程后要执行的代码

如果是只调用run()方法,这个是主线程去执行的,并不是新建的这个线程去执行的

线程的状态,在start()方法调用之前,线程的状态是NEW。 在start()方法调用之后,线程的状态是RUNNABLE。start()是不能被多次调用的。就是线程状态变为RUNNABLE之后,就不能再调用start()方法了。

sleep()

1、方法就是放弃了cpu里的时间片的使用,就是让其他线程去运行,使用cpu,状态也会从Running进入Time Waiting状态

2、其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法就会抛出InterruptedException

3、睡眠结束后的线程未必会立刻得到执行

4、建议用TimeUnit的sleep代替Thread的sleep来获取更好的可读性

  try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

yield()本意是谦让,让出,

1、调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其他线程

2、具体的实现依赖于操作系统的任务调度器

从表面上看,sleep()和yield()都是让线程从running,停止给其他线程,任务调度器只会把时间片分给就绪状态的线程,不会分给阻塞状态的线程。

yield和线程优先级,作用不是很大,cpu的时间片还是有任务调度器来进行分配的。

join()的基本用法,为什么要使用join()方法,作用是等待其他线程结束后,再继续执行主线程方法

static int r = 0;
public static void main(String[] args) throws InterruptedException {
   test1();
}
private static void test1() throws InterruptedException {
   log.debug("开始");
   Thread t1 = new Thread(() -> {
   log.debug("开始");
   sleep(1);
   log.debug("结束");
    r = 10;
});
   t1.start();
   t1.join();
   log.debug("结果为:{}", r);
   log.debug("结束");
}

主线程执行到了t1.join()方法时,就会等待t1线程结束,才会继续执行。如果不加join()方法的,主线程先执行完毕了,t1后执行完毕。作用就是等待某个线程运行结束。

线程的安全问题 使用锁synonized 在代码块或者在方法上

线程间的通信,121212,使用notify()唤醒线程B,使用wait()方法使自己线程A处于等待状态。

volatile

保证内存可见性

不能保证原子性

防止指令重排序,Happen-Before内存模型中,指令重排序可以提高程序执行效率。多线程的时候容易出现问题。可以加volatile防止指令重排序。

synchronized可以保证原子性

懒汉式使用双重判断保证线程安全,和提高性能,同时静态变量使用volatile修饰,防止指令重排序。

目录
相关文章
|
4天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
13 1
|
1天前
|
Java 调度
Java一分钟之线程池:ExecutorService与Future
【5月更文挑战第12天】Java并发编程中,`ExecutorService`和`Future`是关键组件,简化多线程并提供异步执行能力。`ExecutorService`是线程池接口,用于提交任务到线程池,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。通过`submit()`提交任务并返回`Future`对象,可检查任务状态、获取结果或取消任务。注意处理`ExecutionException`和避免无限等待。实战示例展示了如何异步执行任务并获取结果。理解这些概念对提升并发性能至关重要。
15 5
|
1天前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第12天】 在现代软件开发中,多线程编程是提升应用程序性能和响应能力的关键手段之一。特别是在Java语言中,由于其内置的跨平台线程支持,开发者可以轻松地创建和管理线程。然而,随之而来的并发问题也不容小觑。本文将探讨Java并发编程的核心概念,包括线程安全策略、锁机制以及性能优化技巧。通过实例分析与性能比较,我们旨在为读者提供一套既确保线程安全又兼顾性能的编程指导。
|
1天前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
10 3
|
1天前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
51 2
|
1天前
|
安全 Java 调度
Java一分钟:多线程编程初步:Thread类与Runnable接口
【5月更文挑战第11天】本文介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并讨论了多线程编程中的常见问题,如资源浪费、线程安全、死锁和优先级问题,提出了解决策略。示例展示了线程通信的生产者-消费者模型,强调理解和掌握线程操作对编写高效并发程序的重要性。
38 3
|
2天前
|
安全 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第11天】在Java并发编程中,线程安全和性能优化是两个重要的主题。本文将深入探讨这两个方面,包括线程安全的基本概念,如何实现线程安全,以及如何在保证线程安全的同时进行性能优化。我们将通过实例和代码片段来说明这些概念和技术。
3 0
|
2天前
|
Java 调度
Java并发编程:深入理解线程池
【5月更文挑战第11天】本文将深入探讨Java中的线程池,包括其基本概念、工作原理以及如何使用。我们将通过实例来解释线程池的优点,如提高性能和资源利用率,以及如何避免常见的并发问题。我们还将讨论Java中线程池的实现,包括Executor框架和ThreadPoolExecutor类,并展示如何创建和管理线程池。最后,我们将讨论线程池的一些高级特性,如任务调度、线程优先级和异常处理。
|
2天前
|
安全 Java
【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解
【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解
|
2天前
|
安全 Java
【JAVA进阶篇教学】第六篇:Java线程中状态
【JAVA进阶篇教学】第六篇:Java线程中状态