java 多线程 Thread & Runnable 使用与区别

简介: 1.线程和进程的定义进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位

多线程

1.线程和进程的定义

  • 进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
  • 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位
  • 1.2.线程进程的区别体现在几个方面:
  • 因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。


  • 体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。


  • 属于同一个进程的所有线程共享该进程的所有资源,包括文件描述符。而不同的进程相互独立。


  • 线程又称为轻量级进程,进程有进程控制块,线程有线程控制块;


  • 线程必定也只能属于一个进程,而进程可以拥有多个线程而且至少拥有一个线程;


  • 简而言之:开启酷狗音乐只是一个进程,同时可以听歌和下载歌曲(2个线程)

1.3.我们的理解:

  • 进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程,或者更专业化来说:进程是指程序执行时的一个实例。
  • 线程是进程的一个实体。
  • 进程——资源分配的最小单位,线程——程序执行的最小单位

2.多线程的2种或者4种实现方法

ps:网上说有4种实现方法,但是也有一部分人是持但对意见的,那么这4种方法究竟如何呢?


2.1.Java多线程实现的方式有四种

  • 继承Thread类,重写run方法
  • 实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
  • 通过Callable和Future/FutureTask创建多线程
  • 通过线程池创建多线程

补充:

前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果。

后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中

3.多线程的创建之Thread的步骤

  1. 定义一个类继承Thread
  2. 重写run方法
  3. 创建子类对象,就是创建线程对象
  4. 调用start方法,开启多线程并执行,同时还会告诉jvm去调用run方法
package com.Li.xc01;
/**
 * @Description:通过继承thread实现多线程
 * @auther:Li Ya Hui
 * @Time:2021年4月20日下午7:39:10
 */
public class JiSuanQi extends Thread {
  @Override
  public void run() {
//      System.out.println("计算器:run()");
//      System.out.println("当前线程的名字:"+Thread.currentThread().getName());
    for (int i = 0; i < 10; i++) {
      System.out.println("主函数的名字\t"+getName());
            //Thread.currentThread().getName()可以缩写,但是为了语义化,一般都要写
    }
  }
  //此构造期为更改线程名字
  public JiSuanQi(String name) 
  {
    super(name);
  }
}
/**
 * @Description:  测试类 通过继承thread类实现多线程             多线程 之 Thread的步骤
 * @auther:Li Ya Hui
 * @Time:2021年4月20日下午7:17:50
 */
public class Test {
  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      System.out.println("主函数的名字\t"+Thread.currentThread().getName());
    }
    System.out.println("主函数001");
    System.out.println("主函数002");
    System.out.println("主函数003");
    //主函数的线程名字:main
    System.out.println("当前线程的名字"+Thread.currentThread().getName());
    //开启多线程,创建子类对象
    JiSuanQi jiSuanQi = new JiSuanQi("计算器线程");
    //当调用start方法的时候,jvm会自动执行run方法,
    //其他非主函数的线程的名字:Thread-0...... 但是可以通过有参数构造器进行修改
    jiSuanQi.start();
  }
}

通过测试发现thread里的run方法不会因为main方法的结束而受到影响

4.通过实现runnable接口实现多线程

  1. 定义类实现Runnable接口
  2. 覆盖/实现接口中的run方法
  3. 创建Thread类的对象
  4. 将Runnable接口的实现类对象作为参数传递给Thread类的构造函数
  5. 调用Thread类的start方法开启线程
//Runable 接口  的实现类
/**
 * @Description: 通过Runable实现类  给Thread传参数实现 多线程
 * @auther:Li Ya Hui
 * @Time:2021年4月20日下午8:34:21
 */
public class Prims implements Runnable{
  @Override
  public void run() {
    while (true)
    {
      try {
        //线程睡眠 1000毫秒
        Thread.sleep(1000);
        System.out.println("当前线程的内容:"+Thread.currentThread().getName());
      } catch (InterruptedException e) {//线程中断异常
        //异常处理
        System.out.println("当前"+Thread.currentThread().getName()+"线程终端异常");
      }
    }
  }
}
//测试类
package com.Li.xc02;
/**
 * @Description: 测试  runnable  给Thread传参数实现 多线程
 * @auther:Li Ya Hui
 * @Time:2021年4月21日下午3:05:40
 */
public class Test {
  public static void main(String[] args) throws InterruptedException {
    System.out.println("主线程执行的内容001");    
    //实例化runnable,
    Prims a = new Prims();
    //参数形式传递给Thread
    Thread TH = new Thread(a);
    //主线程睡眠
    Thread.sleep(2000);
    System.out.println("主线程执行的内容002");    
    //子线程开启
    TH.start();   
    System.out.println("主线程执行的内容003");
  }
}

4.2.解释说明

Thread类中包括构造函数Thread(Runnable target)和Thread(Runnable target,String name),可以床底Runnable接口,说明构造函数支持传入一个Runnable接口的对象。

构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象。这样的好处是可以完全将Thread对象中的run()方法交由其他线程进行调用。


4.3.比较

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

main函数,实例化线程对象也有所不同,

extends Thread :t.start();

implements Runnable : new Thread(t).start();

使用Runnable,增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类


4.4.实现Runnable的原理

  • 为什么需要定一个类去实现Runnable接口呢?继承Thread类和实现Runnable接口有啥区别呢?


  • 实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖/实现Runnable接口中的run方法,将线程任务代码定义到run方法中。


  • 创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的实现对象,所以将这个实现类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确的得到要运行的线程的任务。


  • 简而言之:实现Runable接口进而实现接口中的run方法,然后创建实例化Thread类,将线程任务所在的对象/类以参数的形式传递给Thread的构造函数最终实现多线程


4.5.实现Runnable的好处

  • 第二种方式,实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
  • 简而言之:避免了单继承的局限性,安全,耦合度低

5.利用匿名内部类+runnable接口实现多线程

package com.Li.xc02;
/**
 * @Description: 利用匿名内部类+runnable接口实现多线程
 * @auther:Li Ya Hui
 * @Time:2021年4月21日下午6:49:12
 */
public class Test02 {
  public static void main(String[] args) {
    System.out.println("主函数线程打印:001");
    Thread thread = new Thread(new Runnable() 
    {
      @Override
      public void run()
      {
        System.out.println("当前线程的名字为:"+Thread.currentThread().getName());
      }
    });
    thread.start();
    System.out.println("主函数线程打印:002");
  }
}

优点:

  1. 线程任务类只是实现了Runnable接口,可以继续继承其他类,而且可以继续实现其他的功能
  2. 同一个线程任务对象可以被包装成多个线程对象
  3. 适合多个相同的程序代码的线程去共享同一个资源
  4. 实现解棍操作,代码可以被多个线程共享,线程任务代码和线程独立


6.Thread和Runnable的区别

6.1.通过继承Thread类模拟售票

package com.Li.xc03;
/**
 * 
 * @Description:买票类   测试通过继承Thread类模拟售票
 * @auther:Li Ya Hui
 * @Time:2021年4月21日下午9:04:36
 */
public class Mythread extends Thread{
  public int tickets = 5;
  public void run() {
    for (int i = 1; i < 10; i++) {
      System.out.println("当前线程为:"+getName()+":"+(tickets--));
    }
  }
  //线程添加名字  
  public Mythread(String name) {
    super(name);
    // TODO Auto-generated constructor stub
  }
}
package com.Li.xc03;
/**
 * @Description: 测试类 测试通过继承Thread类模拟售票
 * @auther:Li Ya Hui
 * @Time:2021年4月21日下午9:13:41
 */
public class Test {
  public static void main(String[] args) {
    Mythread myA = new Mythread("A窗口");
    Mythread myB = new Mythread("B窗口");
    myA.start();
    myB.start();
    //通过结果可以看成是卖了10张票但是一共就5张票,所以此时只能说明这种方式实现的时候是没有共享资源的
  }
}
  • 通过结果可以看成是卖了10张票但是一共就5张票,所以此时只能说明这种方式实现的时候是没有共享资源的 ( 没有共享tickets )

6.2.通过实现Runnable接口实现售票

可以实现共享资源的效果,但是会同时进行买票的问题,延伸出了互斥锁

synchronized (this)//同步锁    //  mutex互斥 在此处是锁的效果,即互斥锁
{
    if (tickets > 0) 
    {
      System.out.println("当前线程的名字:" + Thread.currentThread().getName() + ":" + (tickets--));
    }
}

互斥锁使得,多个线程不可同时访问一个资源

案例展示

package com.Li.xc04;
/**
 * @Description: 通过实现 Runnable接口  实现多线程-模拟售票
 * @auther:Li Ya Hui
 * @Time:2021年4月21日下午9:30:16 
 */
public class Mythread implements Runnable {
  //总票数
  public int tickets = 500;
  @Override
  public void run() {
    for (int i = 0 ;i<10000 ; i++)  {
      //this 指代当前类的意思  在此处表示为给当前的类加上互斥锁的效果
      synchronized (this)//同步锁    //  mutex互斥 在此处是锁的效果,即互斥锁
      {
        if (tickets > 0) {
          System.out.println("当前线程的名字:" + Thread.currentThread().getName() + ":" + (tickets--));
        }
      }
    }
  }
}
package com.Li.xc04;
/**
 * @Description: 测试类  测试Runnable的实现类运用多个线程来共享资源去买票,不会有数据读脏的可能
 * @auther:Li Ya Hui
 * @Time:2021年4月21日下午10:08:55
 */
public class Test {
  public static void main(String[] args) {
    //实例化我的售票类
    Mythread myRunThread1 = new Mythread();
    //实例两个线程  用来分工
    Thread thread1 = new Thread(myRunThread1, "1号窗口");
    Thread thread2 = new Thread(myRunThread1, "线程二");
    thread1.start();
    thread2.start();
    //通过结果我们发现,会有数据读脏的可能性
    //所以要使用互斥锁来避免同时读取数据问题
  }
}

Runnable总结:


  • 实现Runnable接口比继承Thread类所具有的优势:
  • 适合多个相同的程序代码的线程去处理同一个资源
  • 可以避免java中的单继承的限制
  • 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
  • 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类


目录
相关文章
|
5天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
13天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
2天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
|
4天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
4天前
|
Java 开发者
Java多线程编程的艺术与实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的技术文档,本文以实战为导向,通过生动的实例和详尽的代码解析,引领读者领略多线程编程的魅力,掌握其在提升应用性能、优化资源利用方面的关键作用。无论你是Java初学者还是有一定经验的开发者,本文都将为你打开多线程编程的新视角。 ####
|
3天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
9天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
32 9
|
6天前
|
安全 Java 开发者
Java多线程编程中的常见问题与解决方案
本文深入探讨了Java多线程编程中常见的问题,包括线程安全问题、死锁、竞态条件等,并提供了相应的解决策略。文章首先介绍了多线程的基础知识,随后详细分析了每个问题的产生原因和典型场景,最后提出了实用的解决方案,旨在帮助开发者提高多线程程序的稳定性和性能。
|
12天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
9天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
下一篇
无影云桌面