面试中经常被 JAVA 多线程虐?60 多个实例合集,彻底搞懂 Java 多线程!

JAVA 最难学的部分是哪里?很多朋友都会说:「 java 多线程 」。随着业务量和数据的增加,企业不可避免地会使用多线程的方式处理数据。在 Java 职位的面试中,多线程也是必考的高阶知识点之一。

可以说,java 多线程是衡量一名 Java 程序员是否资深的关键标准之一。我们上线了一门新课,通过 15 节实验和 60 多个实例,对 Java 多线程进行一次全面的总结,让你可以百分百地掌握 Java 多线程技术。

Java 多线程技术实战 - https://www.shiyanlou.com/courses/1519

话不多少,一起来学习一下吧!

多进程与多线程的概念

初步创建多线程,理清多线程的概念。

知识点
  • 多线程的概念

  • 创建多线程 —— 继承 Thread

  • 创建多线程 —— 实现 Runnable

  • 创建多线程 —— 实现 Callable

多进程和多线程的概念

进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操作系统的各种功能的进程就是系统进程,而所有由你启动的进程都是用户进程。

多进程

进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操作系统的各种功能的进程就是系统进程,而所有由你启动的进程都是用户进程。

如图所示每一个正在运行的 .exe 程序都是一个进程。

多线程

进程就是有一个或多个线程构成的。而线程是进程中的实际运行单位,是独立运行于进程之中的子任务。是操作系统进行运算调度的最小单位。可理解为线程是进程中的一个最小运行单元。

进程和线程之间的关系

一个进程下包含 N 个线程。

举例说明:玩英雄联盟的时候,打开客户端便启动了许多个线程:排队队列线程、好友聊天线程、正在支付线程。在英雄联盟这一个进程之下便启动了 N 个线程。

我们初学 java 边写代码的时候,通常使用 main 方法进行运行,此时 main 方法执行的便是一个主线程,而所谓的多线程,即是在主线程执行的过程中,同时执行其他的线程。

但是同时执行多个线程容易出现报错现象,例如同时同分同秒,两个线程同时修改一个 txt、数据库表文件,或第一个线程没有修改完 txt、数据库表文件,第二个线程同时也去修改。这便是线程之间的混乱、资源竞争、脏读,便是程序员需要去解决的疑难杂症。

创建多线程 —— 继承 Thread

java 世界中有两种方式创建多线程:

java 世界中有两种方式创建多线程,分别是继承 Thread 类,实现 Runnable 接口。

继承 Thread 类方式创建多线程

第一步:在 webide 上右键单击菜单,选择 New File 创建新文件。

第二步:创建文件名为 test0.java

第三步:编写 test0.java 中继承 Thread 类方式创建多线程的代码如下所示:

public class test0 {
    public static void main(String[] args) {
        Thread MyThread = new MyThread();
        MyThread.start();
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello myThread" + Thread.currentThread().getName());
    }
}

第四步:编译 test0.java 代码:

javac test0.java

编译之后,会产生我们所编写的 test0 类与 MyThread 类

第五步:运行 test 代码:

java test0

创建多线程 —— 实现 Runnable

只需要把《创建多线程 —— 继承 Thread》中代码修改成如下所示即可,其它操作不变:

public class test0 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println("hello myRunnable" + Thread.currentThread().getName());
    }
}

执行结果如下所示:

通常情况下,如果创建的线程类已经含有父类时候,此时由于 Java 语法结构不支持多继承的原因,不能够再次继承 Thread 类,此时则需要使用实现 Runnable 接口的方式来应对如此场景。

另外值得说明的是,Thread 类也实现了 Runnable 接口。

实现多线程传参 —— 有参构造

由于多线程是由继承 Thread 或实现 Runnable 并重写 run() 方法,通过 thread.start() 进行运行的,而本身重写的 run() 方法是不具备传参能力的,那我新建的线程就接受不到我所想传入的参数了么?

创建 study1.java 文件
class ThreadA extends Thread{
    private String age;
    public ThreadA(String age){
        this.age = age;
    }
    @Override
    public void run() {
        System.out.println("age=" + age);
    }
}
public class study1 {
    public static void main(String[] args) {
        String age = new String("12");
        ThreadA a = new ThreadA(age);
        a.start();
    }
}

无论 extendsThread 还是 implementsRunnable ,传参都需要使用线程初始化的有参构造形式,达到多线程传参的目的。也可以做到重载有参构造,传入各式对象。

study1 运行结果

实现多线程返回值 —— 实现 Callable<V>

通常意义上理解确实 Java 实现多线程的方式有继承 Thread 和实现 Runnable,但是如果想实现多线程并且具有返回值的情况下,需要实现 Callable<V> 接口,这个接口是 JDK1.5 版本以后才出现的接口。

创建 study2.java

创建 study2.java 文件,利用实现 Callable<V> 进行返回,代码如下所示:

import java.util.concurrent.Callable;
public class study2 {
    public static void main(String[] args) {
        MyCallable MyCallable = new MyCallable("张方兴");
        String call = null;
        try {
            call = MyCallable.call();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(call);
    }
}
class MyCallable implements Callable<String>{
    private String name;
    public MyCallable(String name) {
        this.name = name;
    }
    @Override
    public String call() throws Exception {
        return "call:" + name;
    }
}
study2 运行结果

Callable<V> 接口详解

一般继承 Thread 的类,含有 .start() 函数,所以直接可以使用 .start() 函数进行启动。

实现 Runnable 的类,需要通过 newThread(myRunnable).start(); 的方式进行启动,即实现 Runnable 的类只是做好了一段多线程所需执行的内容,自身并没有执行的能力,需要通过 Thread 类的 .start() 函数进行启动。

实现 Callable<V> 的接口,含有 .call() 函数,所以可以直接使用 .call() 函数进行启动,另外值得说明的是, Callable<V> 函数具有返回值,返回值为定义类时使用的 <V> 类型,其定义是其返回。 Callable<V> 接口定义如下所示:

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

Callable<V> 用于指示接口类型声明是由 Java 语言规范定义的功能接口。从概念上讲,函数接口只有一个抽象方法。因为 java.lang.reflect.Method#isDefault()default methods 有一个实现,所以它们不是抽象的。如果接口声明一个抽象方法重写 java.lang.Object 的一个公共方法,则该方法也不计入接口的抽象方法计数,因为接口的任何实现都将具有来自 java.lang.Object 或其他位置的实现。

另外注意,函数接口的实例可以使用 lambda 表达式、方法引用或构造函数引用创建。

Callable<V> 在需要使用返回值的情况下,程序是同步运行的

Callable<V> 其它情况下,程序是异步运行的

完整课程内容,请在实验楼边操作边学习。

Java 多线程技术实战 - https://www.shiyanlou.com/courses/1519

????????????点击阅读原文,学习完整训练营课程

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值