https://zhuanlan.zhihu.com/p/107479862java
JAVA 最难学的部分是哪里?不少朋友都会说:「 java 多线程 」。程序员
随着业务量和数据的增长,企业不可避免地会使用多线程的方式处理数据。在 Java 职位的面试中,多线程也是必考的高阶知识点之一。能够说,java多线程是衡量一名 Java 程序员是否资深的关键标准之一。web
今天,咱们就来学习一下 Java 多线程的概念吧!面试
(点击课程连接,开启实验环境,边学边练才是更有效的学习方式)数据库
Java 多线程技术实战 www.shiyanlou.com初步建立多线程,理清多线程的概念。多线程
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操做系统的各类功能的进程就是系统进程,而全部由你启动的进程都是用户进程。
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。凡是用于完成操做系统的各类功能的进程就是系统进程,而全部由你启动的进程都是用户进程。异步
如图所示每个正在运行的 .exe
程序都是一个进程。ide
进程就是有一个或多个线程构成的。而线程是进程中的实际运行单位,是独立运行于进程之中的子任务。是操做系统进行运算调度的最小单位。可理解为线程是进程中的一个最小运行单元。函数
一个进程下包含 N 个线程。学习
举例说明:玩英雄联盟的时候,打开客户端便启动了许多个线程:排队队列线程、好友聊天线程、正在支付线程。在英雄联盟这一个进程之下便启动了 N 个线程。
咱们初学 java 边写代码的时候,一般使用 main 方法进行运行,此时 main 方法执行的即是一个主线程,而所谓的多线程,便是在主线程执行的过程当中,同时执行其余的线程。可是同时执行多个线程容易出现报错现象,例如同时同分同秒,两个线程同时修改一个 txt、数据库表文件,或第一个线程没有修改完 txt、数据库表文件,第二个线程同时也去修改。这即是线程之间的混乱、资源竞争、脏读,即是程序员须要去解决的疑难杂症。
java 世界中有两种方式建立多线程,分别是继承 Thread 类,实现 Runnable 接口。
第一步:在 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
只须要把《建立多线程 —— 继承 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
,传参都须要使用线程初始化的有参构造形式,达到多线程传参的目的。也能够作到重载有参构造,传入各式对象。
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;
}
}
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 多线程技术实战 www.shiyanlou.com