【Java|多线程与高并发】详解start()方法和run()方法的区别

简介: 本篇文章主要讲解以下几个问题:start()方法和run()方法的区别与联系 为什么不能连续两次调用shart()方法 run()方法中可能忽略的问题

前言

本篇文章主要讲解以下几个问题:

start()方法和run()方法的区别与联系

为什么不能连续两次调用shart()方法

run()方法中可能忽略的问题


1.start()方法和run()方法


我们在创建线程时,会重写run()方法.run()方法可以理解为线程要做的任务,但是直接调用run()方法,只是main线程也就是主线程去执行的,是没有新线程产生的

如果要想让线程去执行run()方法里面的代码,就需要让创建线程的对象去调用start()方法,shart()方法可以创建并启动线程,JVM调用run()方法后(后面会有介绍) 线程才会去执行


2.不能两次调用start()方法


同一个线程对象,只能调用一次start()方法,不能两次调用start()方法,调用两次的话,会抛出 IllegalThreadStateException 这个异常

示例如下:

public class Example6 {

   public static void main(String[] args) {

       Thread t1 = new Thread(()->{

           System.out.println("1");

       });

       t1.start();

       t1.start();

   }

}

111.png

为什么会这样?我们可以查看start()的源码

112.png

上面有一段话: A zero status value corresponds to state "NEW" 意思是 零状态值对应于状态“NEW”

线程运行时有六种状态,先熟悉一下,之后还会写文章进行详细介绍:

状态 描述

新建(NEW) 表示线程已经创建好了,但是还没有调用start()方法

就绪(RUNNABLE) 表示线程可能在运行,也可能在就绪队列

阻塞 (BLOCKED) 表示线程处于等在锁的状态

等待(WAITING) 表示线程处于条件等待状态,当触发条件后会唤醒

计时等待(TIME_WAIT) 比WAITING多了个超时条件触发的机制

终止(TERMINATED) 表示线程执行结束

因此线程再调用start()方法之后,可能处于终止,或者其它非NEW状态,第二次调用的时候,相当于重新让线程运行一遍,从线程安全和线程本身的执行逻辑来看,都是不合理的,因此在调用start()方法之后,会对线程的状态进行一个判断,如果线程不是在NEW状态下,就会抛出异常


3.线程的执行是随机的


线程的执行是随机的,这也是个老生常谈的问腿了,究其原因还是因为线程的"抢占式执行",谁先"抢"到

操作系统分配的CPU资源,谁先去执行


start()方法和run()方法的执行顺序不一定相同


示例1:

看一下代码及代码运行结果

class MyThread extends Thread{

   private int val;

   public MyThread(int val) {

       this.val = val;

   }

   @Override

   public void run() {

       System.out.println(val);

   }

}

public class Example1 {

   public static void main(String[] args) {

       MyThread myThread1 = new MyThread(1);

       MyThread myThread2 = new MyThread(2);

       MyThread myThread3 = new MyThread(3);

       MyThread myThread4 = new MyThread(4);

       MyThread myThread5 = new MyThread(5);

       myThread1.start();

       myThread2.start();

       myThread3.start();

       myThread4.start();

       myThread5.start();

   }

}

114.png

由此我们可以看到虽然是myThread1先调用的start()方法,但是输出的结果却是在第二位,而myThread3后执行却在第一位

115.png

再次运行一次代码的执行结果虽然与上次不同,但仍然是随机的,当然也不是没有运行结果是1 2 3 4 5的可能


示例2:

看一下代码及代码运行结果

public class Example4 {

   public static void main(String[] args) {

       Thread t1 = new Thread(()->{

           while (true){

               System.out.println("t1");

           }

       });

       Thread t2 = new Thread(()->{

           while (true){

               System.out.println("t2");

           }

       });

       t1.start();

       t2.start();

   }

}

117.png

这里我们也能看到,start()执行的顺序与run()方法执行的顺序无关,随机执行t1和t2的run()方法 虽然这里是while(true),但是线程之间是相互独立的,所以并不影响

因此可以得出结论:start()方法和run()方法的执行顺序不一定相同


4.run()方法由JVM调用


start()执行完后,创建的新线程不会立刻去执行run()方法, run()方法会先被JVM去调用,之后对应的线程才会去执行.


public Thread(Runnable target)中的target

之前再讲创建线程的5种方法,介绍了实现Runnable接口,创建线程的方法

117.png

但其实这里面有一个坑,那就是public Thread(Runnable target)中的target参数,来看一下run()方法的底层源码

118.png

如果这里面的target不为空,才会去执行target的run()方法.如果传一个null,就不会去执行,如图所示:

120.png

代码如下:

class MyRunnable implements Runnable{

   @Override

   public void run() {

       System.out.println("1");

   }

}

public class Example7 {

   public static void main(String[] args) {

       MyRunnable myRunnable1 = new MyRunnable();

       MyRunnable myRunnable2 = null;

       Thread t1 = new Thread(myRunnable1);

       t1.start();

       Thread t2 = new Thread(myRunnable2);

       t2.start();

   }

}

因此要注意使用继承Runnable接口 创建线程的时候 要注意target不要为null

相关文章
|
1天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
3天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
14 4
|
3天前
|
消息中间件 供应链 Java
掌握Java多线程编程的艺术
【10月更文挑战第29天】 在当今软件开发领域,多线程编程已成为提升应用性能和响应速度的关键手段之一。本文旨在深入探讨Java多线程编程的核心技术、常见问题以及最佳实践,通过实际案例分析,帮助读者理解并掌握如何在Java应用中高效地使用多线程。不同于常规的技术总结,本文将结合作者多年的实践经验,以故事化的方式讲述多线程编程的魅力与挑战,旨在为读者提供一种全新的学习视角。
21 3
|
4天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
14 1
|
4天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
8天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
15 3
|
6月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
5月前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
476 0
|
3月前
|
监控 算法 Java
企业应用面临高并发等挑战,优化Java后台系统性能至关重要
随着互联网技术的发展,企业应用面临高并发等挑战,优化Java后台系统性能至关重要。本文提供三大技巧:1)优化JVM,如选用合适版本(如OpenJDK 11)、调整参数(如使用G1垃圾收集器)及监控性能;2)优化代码与算法,减少对象创建、合理使用集合及采用高效算法(如快速排序);3)数据库优化,包括索引、查询及分页策略改进,全面提升系统效能。
45 0
|
5月前
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【6月更文挑战第30天】Java分布式锁在高并发下确保数据一致性,通过Redis的SETNX、ZooKeeper的临时节点、数据库操作等方式实现。优化策略包括锁超时重试、续期、公平性及性能提升,关键在于平衡同步与效率,适应大规模分布式系统的需求。
161 1