26、JAVA进阶——多线程

本文涉及的产品
文档翻译,文档翻译 1千页
文本翻译,文本翻译 100万字符
图片翻译,图片翻译 100张
简介: 26、JAVA进阶——多线程

一、认识进程和线程


      计算机的操作系统大多采用多任务和分时设计,多任务是指在一个操作系统中可以同时运行多个程序。


      例如在使用QQ聊天的同时听音乐,即有多个独立运行的任务,每个任务对应一个进程,每个进程又可以产生多个线程


1. 进程

(1)程序(Program)是对数据描述与操作的代码的集合,如Office中的Word、暴风影音等应用程序。


(2)进程(Process)是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完成过程,这个过程也是进程本身从产生、发展至消亡的过程。


(3)操作系统同时管理一个计算机系统中的多个进程,让计算机系统中的多个进程轮流使用CPU资源,或者共享操作系统的其它资源。


(4)进程有如下特点:


       -->进程是系统运行程序的基本单位。


       -->每一个进程都有自己独立的一块内存空间、一组系统资源。


       -->每一个进程的内部数据和状态都是完全独立的。


2. 线程

(1)线程是进程中执行运算的最小单位,一个进程在其执行过程中可以产生多个线程,而线程必须在某个进程内执行。


(2)线程是进程内部的一个执行单元,是可完成一个独立任务的顺序控制流程,如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程。


(3)线程按处理级别可以分为核心级线程和用户级线程。


--》核心级线程


     -->核心级线程是和系统任务相关的线程,它负责处理不同进程之间的多个线程。


     -->允许不同进程中的线程按照同一相对优先调度方法对线程进行调度,使它们有条不紊地工作,可以发挥多处理器的并发优势,以充分利用计算机的软/硬件资源。


--》用户级线程


     -->在开发程序时,由于程序的需要而编写的线程即用户级线程,这些线程的创建、执行和消亡都是编写在应用程序时进行控制的。


     -->对于用户级线程的切换,通常发生在一个应用程序的诸多线程之间,如迅雷中的多线程下载就属于用户线程。


     -->多线程可以改善用户体验。具有多个线程的进程能更好地表达和解决现实世界的具体问题,多线程是计算机应用开发和程序设计的一项重要的实用技术。


(4)线程和进程既有联系又有区别:


--》一个进程中至少要有一个线程。


--》资源分配给进程,同一进程的所有线程共享该进程的所有资源。


--》处理机分配给线程,即真正在处理机上运行的是线程。


3. 多线程的优势

--》多线程程序可以带来更好的用户体验,避免因程序执行过慢而导致计算机出现计算机死机或者白屏的情况。


--》多线程程序可以最大限度地提高计算机系统的利用效率。如迅雷的多线程下载。

二、编写线程类


(1)每个程序至少自动拥有一个线程,称为主线程。


(2)当程序加载到内存时启动主线程


(3)Java程序中的public static void main()方法是主线程的入口,运行Java程序时,会先执行这个方法。


(4)开发中,用户编写的线程一般都是指除了主线程之外的其他线程。


(5)使用一个线程的过程可以分为以下4个步骤:


第一步:定义一个线程,同时指明这个线程所要执行的代码,即期望完成的功能。


第二步:创建线程对象。


第三步:启动线程


第四部:终止线程。


(6)定义一个线程类通常有两种方法,分别是继承java.lang.Thread类和实现java.lang.Runnable接口。


1. 使用Thread类创建线程


       --》Java提供了java.lang.Thread类支持多线程编程,该类提供了大量的方法来控制和操作线程,常用方法如下:


       --》创建线程时继承Thread类并重写Thread类中的run()方法。


       --》Thread类的run()方法是线程要执行操作任务的方法,所以线程要执行的操作代码都需要写在run()方法中,并通过调用start()方法来启动线程。



2. 使用Runnable接口创建线程


       --》使用继承Thread类的方式创建线程简单明了,符合大家的习惯,但它有一个缺点,如果定义的类已经继承了其他类则无法再继承Thread类。使用Runnable接口创建线程的方式可以解决上述问题。


       --》Runnable接口中声明了一个run()方法,即public void run()。


       --》一个类可以通过实现Runnable接口并实现run()方法完成线程的所有活动,已实现的run()方法称为该对象的线程体。


       --》任何一个实现Runnable接口的对象都可以作为一个线程的目标对象。


public class MyThread extends Thread {
  @Override
  public void run() {
  for (int i = 1; i <=10; i++) {
    System.out.println(Thread.currentThread().getName()+"->"+i);
  }
  }
}
public class Test {
  public static void main(String[] args) {
  MyThread mt1=new MyThread();
  mt1.start();
  MyThread mt2=new MyThread();
  mt2.start();
  }
}
public class MyThread implements Runnable {
  @Override
  public void run() {
  for (int i = 1; i <=10; i++) {
    System.out.println(Thread.currentThread().getName()+"->"+i);
  }
  }
}
public class Test {
  public static void main(String[] args) {
  MyThread mt=new MyThread();
  Thread thread1=new Thread(mt);
  thread1.start();
  Thread thread2=new Thread(mt);
  thread2.start();
  }
}

(7)两种创建线程的方式有各自的特点和应用领域:


       --》直接继承Thread类的方式编写简单,可以直接操作线程,适用于单重继承的情况;


       --》实现Runnable接口的方式,当一个线程继承了另一个类时,就只能用实现Runnable接口的方法来创建线程,而且这种方式还可以使多个线程之间使用同一个Runnable对象。


三、线程状态和线程调度


1.线程的状态

(1)线程的生命周期可以分为4个阶段,即线程的4种状态,分别为新生状态、可运行状态、阻塞状态和死亡状态。


(2)一个具有生命的线程,总是处于上述4种状态之一。


1. 新生状态(New Thread)


       创建线程对象之后,尚未调用其start()方法之前,这个线程就有了生命,此时线程仅仅是一个空对象,系统没有为其分配资源。此时只能启动和终止线程,任何其它操作都会引发异常。


2. 可运行状态(Runnable)


       a.当调用了start()方法启动线程之后,系统为该线程分配除CPU外的所需资源,这个线程就有了运行的机会,线程处于可运行的状态,在这个状态当中,该线程对象可能正在运行,也可能尚未运行。


       b.对于只有一个CPU的机器而言,任何时刻只能有一个处于可运行状态的线程占用处理机,获得CPU资源,此时系统真正运行线程的run()方法。


3. 阻塞状态(Blocked)


       a.一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态。


       b.阻塞状态是一种“不可运行”的状态,而处于这种状态的线程在得到一个特定的事件之后会转回可运行状态。


       c.导致一个线程被阻塞有以下原因:


               --》调用了Thread类的静态方法sleep()。


               --》一个线程执行到一个I/O操作时,如果I/O操作尚未完成,则线程将被阻塞。


               --》如果一个线程的执行需要用一个对象的锁,而这个对象的锁正被别的线程占用,那么此线程被阻塞。


               --》线程的suspend()方法被调用而使线程被挂起时,线程进入阻塞状态。但suspend()容易导致死锁,已经被JDK列为过期方法,基本不再使用。


       d.处于阻塞状态的线程可以转回到可运行状态,例如,在调用sleep()方法之后,这个线程的睡眠时间已经达到了指定的间隔,那么它就有可能重新回到可运行状态。或当一个线程等待的锁变得可用的时候,那么这个线程也会从被阻塞的状态转入可运行状态。



public class Wait {
  public static void bySec(long s) {
  for (int i = 1; i <= s; i++) {
    System.out.println(i + "秒");
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
  }
  }
}
public class Test {
  public static void main(String[] args) {
  System.out.println(Thread.currentThread().getName() + "线程开始");
  Wait.bySec(10);
  System.out.println(Thread.currentThread().getName() + "线程结束");
  }
}


4. 死亡状态(Dead)


       一个线程的run()方法运行完毕、stop()方法被调用或者在运行过程中出现未捕获的异常时,线程进入死亡状态。

2.线程调度

(1)当同一时刻有多个线程处于可运行状态,它们需要排队等待CPU资源,每个线程会自动获得一个线程的优先级(Priority),优先级的高低反映线程的重要或紧急程度。


(2)可运行的线程按优先级排队,线程调度依据建立在优先级基础上的“先到先服务”原则。


(3)线程调度管理器负责线程排队和在线程间分配CPU,并按线程调度算法进行调度。当线程调度管理器选中某个线程时,该线程获得CPU资源进入运行状态。


(4)线程调度是抢占式调度,即在当前线程执行过程中如果有一个更高优先级的线程进入可运行状态,则这个更高优先级的线程立即被调度执行。


1. 线程优先级


       -->线程的优先级用1~10表示,10表示优先级最高,默认值是5。


       -->每个优先级对应一个Thread类的公用静态常量。


               --》public static final int NORM_PRIORITY=5;


               --》public static final int MIN_PRIORITY=1;


               --》public static final int MAX_PRIORITY=10;


       -->每个线程的优先级都介于Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间


       -->线程的优先级可以通过setPriority(int grade)方法更改,此方法的参数表示要设置的优先级,它必须是一个1-10之间的整数。


2. 实现线程调度的方法


       -->join()方法


       join()方法使当前线程暂停执行,等待调用该方法的线程结束后再继续执行本线程。它有3种重载形式:


               --》public final void join()


               --》public final void join(long mills)


               --》public final void join(long mills,int nanos)



public class MyThread implements Runnable {
  @Override
  public void run() {
  for (int i = 1; i <=10; i++) {
    Thread.currentThread().setName("name"+i);
    System.out.println(Thread.currentThread().getName());
  }
  }
}


public class Test {
  public static void main(String[] args) {
  MyThread mt = new MyThread();
  Thread thread1 = new Thread(mt);
  for (int i = 1; i <= 10; i++) {
    if (i == 5) {
    thread1.start();
    try {
      thread1.join();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    }
    System.out.println(Thread.currentThread().getName()+i);
  }
  }
}


       -->sleep()方法


       sleep()方法会让当前线程睡眠(停止执行)millis毫秒,线程由运行中的状态进入不可运行状态,睡眠时间过后线程会再次进入可运行状态。语法结构如下:


               --》public static void sleep(long millis)


       -->yield()方法


       yield()方法可让当前线程暂停执行,允许其它线程执行,但该线程仍处于可运行状态,并不变为阻塞状态。此时,系统选择其他相同或更高优先级线程执行,若无其它相同或更高优先级线程,则该线程继续执行。


public class MyThread implements Runnable {
  @Override
  public void run() {
  for (int i = 1; i <=10; i++) {
    System.out.println(Thread.currentThread().getName()+"->"+i);
    if(i==5){
    System.out.println("线程礼让");
    Thread.yield();
    }
  }
  }
}



public class Test {
  public static void main(String[] args) {  
  MyThread mt=new MyThread();
  Thread thread1=new Thread(mt);
  thread1.start();
  Thread thread2=new Thread(mt);
  thread2.start();
  }
}

       -->sleep()方法和yield()方法的区别

1.png




相关文章
|
6天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
2天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
15 9
|
5天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
2天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
5天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
15 3
|
4天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
5天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
14 1
|
5天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
6天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
27 1
|
9天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####