Java知识点复习(七)多线程

  1. 1. 多线程
    1. 1.1. 1、进程与线程的概念
      1. 1.1.1. 1.1、进程是什么?
      2. 1.1.2. 1.2、线程是什么?
      3. 1.1.3. 1.3、Java中谁是线程谁是进程?
      4. 1.1.4. 1.4、并发和并行的概念以及区别
        1. 1.1.4.1. 1.4.1、并发是什么?
        2. 1.1.4.2. 1.4.2、并行是什么?
        3. 1.1.4.3. 1.4.3、并发和并行的区别
      5. 1.1.5. 1.5、并发编程和并行编程的概念
    2. 1.2. 2、Java中线程创建的几种方式(掌握)
      1. 1.2.1. 2.1、Java中线程类概述
        1. 1.2.1.1. 2.1.1、Thread类的构造方法
        2. 1.2.1.2. 2.1.2、Thread类的常用方法
        3. 1.2.1.3. 2.1.3、Thread类的静态常量
        4. 1.2.1.4. 2.1.4、线程的常用设置获取方法
      2. 1.2.2. 2.2、第一种:继承Thread类
        1. 1.2.2.1. 2.2.1、创建多线程的步骤
        2. 1.2.2.2. 2.2.2、创建多线程的具体代码
      3. 1.2.3. 2.3、第二种:实现Runnable接口重写run方法,以及使用内部类创建Runnable接口
        1. 1.2.3.1. 2.3.1、实现Runnable步骤
        2. 1.2.3.2. 2.3.2、实现Runnable创建多线程具体代码
      4. 1.2.4. 2.4、第三种:实现Callable接口创建线程
        1. 1.2.4.1. 2.4.1、实现Callable创建线程步骤
        2. 1.2.4.2. 2.4.2、实现Callable创建线程代码案例
      5. 1.2.5. 2.5、三种创建线程的优缺点与区别以及常用的是哪个
        1. 1.2.5.1. 2.5.1、继承Thread类的优缺点
        2. 1.2.5.2. 2.5.2、实现Runnable接口的优缺点
        3. 1.2.5.3. 2.5.3、实现Callable接口创建FutureTask对象的优缺点
      6. 1.2.6. 2.6.多线程注意事项

多线程

1、进程与线程的概念

1.1、进程是什么?

进程是在操作系统上体现的,进程就是应用程序,例如:QQ、微信、王者荣耀,只要是应用程序并且打开了就是一个进程。

1.程序的概念:
程序是一个静态的概念,代表的是系统中的应用程序的启动程序(exe等结尾的可执行文件)一旦执行程序系统就会生成一个进程。

2.进程的概念:
执行中的程序就是进程,此进程是一个动态的概念–>进程中会有执行内容(运行状态会有交互)并且赋予一定的独立功能

电脑中进程是单个还是多个?
1.电脑中的进程是多个–>多进程

3.多进程的概念:
多进程就是在电脑的运行状态下,可以打开多个应用程序开启多个进程,进程之间不会有相互干扰,并且可以同时运行,进程的运行要看的是CPU的核心数–>一个核心代表你可以开启一个进程,所有的进程都会抢占CPU的执行权–>所以软件程序开的越多越消耗CPU的资源

1.2、线程是什么?

线程的概念:在应用程序中体现,并且应用程序(进程)想要执行就必须有一个线程(一般这个线程叫做主线程),线程的体现在应用程序运行。

比如:打开QQ时在一个聊天窗口那QQ会不会继续接收其他信息?–>如果QQ打开一个聊天窗口则代表开启了一个线程

1.单线程:一个应用程序中只有一个线程,也就是在执行时不能同时执行其他任务,只能等待第一个任务结束才能继续
2.多线程:在一个应用程序中可以执行多个任务

1.3、Java中谁是线程谁是进程?

Java运行是使用JRE–>JVM虚拟机中运行–JVM虚拟机是进程–>JVM虚拟机是多线程的,所以Java虚拟机执行时会出现各种的溢出(执行超载了),我们所写所有的程序是线程
JVM虚拟机是进程,我们写的可执行代码是线程

1.4、并发和并行的概念以及区别

1.4.1、并发是什么?

并发是指同一时刻只能一条指令执行,但是多个进程/线程在执行时使用轮询方式快速切换执行,使得宏观上显示的是多个进程/线程同时执行,但是在微观上则不是同时执行,而是交替执行。

1.4.2、并行是什么?

并行是指同一时刻可以执行多条指令,在宏观意义上是同时执行,同样在微观意义上也是同时执行

1.4.3、并发和并行的区别

1.并发:同时刻只能执行一条指令
2.并行:同时刻可以执行多条指令

  • 注: 在使用多线程时,一定要明白什么是并发什么是并行,并且这俩不能搞混–>什么是并发编程什么是并行编程

1.5、并发编程和并行编程的概念

1.在CPU执行繁忙时资源不足时,则这时开启多进程代表就是并发,如果程序开启较少时CPU可以实现意义上的并行执行
2.在单核CPU中执行多个程序时是由操作系统切换执行
3.在多核CPU中执行多个程序时是可以并行执行多个程序

2、Java中线程创建的几种方式(掌握)

Java中创建线程分为三种方式,并且三种方式有异同,最终其中方式相同,功能以及实现会有所不同

2.1、Java中线程类概述

Java创建多线程使用Thread类,其中Thread是线程的启动与实现方式,并且定义了一些静态方法供开发人员使用
Thread类是直接实现于Runnable接口,Runnable接口是线程祖先

2.1.1、Thread类的构造方法


// 使用空参构造方法创建多线程,并且使用默认线程名称
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
// 指定线程名名称创建多线程
public Thread(String name) {
init(null, null, name, 0);
}
// 使用Runnable内部类创建或使用实现Runnable的子类创建多线程
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
// 使用Runnable内部类创建或使用实现Runnable的子类创建多线程,并且指定名称
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
// 使用Runnable内部类创建或使用实现Runnable的子类创建多线程,并且指定分组
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
// 创建多线程并指定分组名称以及线程名称
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
// 使用Runnable内部类创建或使用实现Runnable的子类创建多线程,并指定分组名称以及线程名称
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}

2.1.2、Thread类的常用方法

1.public void run() : 线程执行的方法–>线程中需要写的逻辑代码,并且不能调用此run方法,run方法是由虚拟机调用执行。
2.public synchronized void start(): 线程启动方法–>启动线程并自动执行run方法,使该线程开始执行;Java虚拟机调用该线程的run方法。

2.1.3、Thread类的静态常量

  1. public final static int MIN_PRIORITY = 1;:设置线程最低优先级
  2. public final static int NORM_PRIORITY = 5;:设置线程默认优先级
  3. public final static int MAX_PRIORITY = 10;:设置线程最高优先级

2.1.4、线程的常用设置获取方法

  1. public long getId():获取当前执行的线程id
  2. public final String getName():获取当前执行的线程名称
  3. public final int getPriority():获取当前执行的线程优先级
  4. public State getState():获取当前执行的线程状态
  5. public final ThreadGroup getThreadGroup():获取当前执行的线程分组
  6. public final void setDaemon(boolean on):是否开启守护线程
  7. public final synchronized void setName(String name):设置线程的名称
  8. public final void setPriority(int newPriority):设置线程优先级
  9. public static native Thread currentThread():获取当前线程–>获取非继承Thread类的线程

2.2、第一种:继承Thread类

2.2.1、创建多线程的步骤

1.创建一个类,继承Thread类
2.实现Thread类中run方法->run方法是多线程的执行体(线程执行的逻辑代码)
3.创建测试类,并继承了Thread类的类对象
4.调用ctart方法启动线程

2.2.2、创建多线程的具体代码

1.创建自定义类继承Thread类

public class MyThread extends Thread{  
private String name;

public MyThread(String name) {
super(name);
this.name = name;
}

public MyThread() {
} //如果想要实现多线程 必须重写run方法

@Override
public void run() {
//注释父类的调用自己实现
//super.run();
//写一个for循环代表多线程的执行逻辑
for (int i = 0; i < 10; i++) {
System.out.println(name+"线程执行:"+i);
}
}
}

2.创建测试方法并创建线程启动

public class ThreadDemo {  
public static void main(String[] args) {
//创建多线程对象
MyThread myThread1=new MyThread("线程1");
//启动线程
//错误示范:
//myThread1.run();
//正确启动方式
myThread1.start();

MyThread myThread2=new MyThread("线程2");
myThread2.start();

System.out.println("程序结束...");
}
}

2.3、第二种:实现Runnable接口重写run方法,以及使用内部类创建Runnable接口

2.3.1、实现Runnable步骤

1.创建线程类,并实现Runnable
2.重写run方法并编写逻辑代码
3.创建测试类,创建实现Runnable接口的类
4.创建Thread,并将线程类做参数传入
5.启动线程测试

2.3.2、实现Runnable创建多线程具体代码

1.线程类实现Runnable接口

public class MyThread implements Runnable{  

//多线程执行的逻辑的方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("多线程执行:"+i);
}
}
}

2.线程测试类

public class ThreadDemo {  
public static void main(String[] args) {
//创建线程类
MyThread myThread=new MyThread();
//创建线程对象
Thread thread1=new Thread(myThread);
Thread thread2=new Thread(myThread);

//启动线程
thread1.start();
thread2.start();

//使用内部类创建多线程
Thread thread3=new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("匿名内部类多线程执行:"+i);
}
}
});
//启动线程
thread3.start();
System.out.println("程序结束");
}
}

2.4、第三种:实现Callable接口创建线程

2.4.1、实现Callable创建线程步骤

第三种方式与前两不同,这里没有run方法,而是使用call方法去实现线程的逻辑代码,并且可以拥有返回值,但是接收返回值时会造成线程的阻塞(暂停等待接收数据),同样不能通过同一个FutureTask对象创建两个多线程

创建方式:
1.创建线程类实现Callable接口,并实现call方法
2.通过FutureTask类创建线程–>传入Callable的实现类
3.创建Thread线程对象,并将FutureTask对象传入当做参数
4.启动线程
5.接收线程返回值

2.4.2、实现Callable创建线程代码案例

1.创建线程类实现Callable接口

//实现Callable接口并指定泛型类型-->泛型类型是Value,并且是多线程的返回值  
import java.util.concurrent.Callable;

public class MyThread implements Callable<String> {
/**
* call方法与之前的两种方式中的 run方法 相同,都是线程的执行体
* 不同点在于call有返回值
*
* @return 返回值
* @throws Exception 线程的异常
*/

@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println("线程执行"+i);
}
return "线程执行完毕";
}
}

2.创建测试类启动线程

import java.util.concurrent.FutureTask;  

public class ThreadDemo {
public static void main(String[] args) throws Exception {
//创建线程
//1.创建实现Callable接口的类
MyThread myThread = new MyThread();
// 2. 使用FutureTask创建此线程,泛型类型与Callable实现时的相同
// 参数为Callable实现类
FutureTask<String> futureTask1=new FutureTask<>(myThread);
FutureTask<String> futureTask2=new FutureTask<>(myThread);
//3.创建线程类
Thread thread1=new Thread(futureTask1);
//4.启动线程
thread1.start();
Thread thread2=new Thread(futureTask2);
thread2.start();

// 5. 接收线程返回的参数使用public V get() throws InterruptedException, ExecutionException方法
// get方法会造成程序阻塞(会停止),等待接收线程返回的数据
String result1 = futureTask1.get();
System.out.println(result1);
String result2=futureTask2.get();
System.out.println(result2);

//最后输出程序结束
System.out.println("程序结束");
}
}

2.5、三种创建线程的优缺点与区别以及常用的是哪个

2.5.1、继承Thread类的优缺点

1.优点:继承后直接可以启动线程
2.缺点:Java是单继承的语言一旦继承了则无法继承其他的类的.造成我们的可扩展性非常低并且没有返回值

2.5.2、实现Runnable接口的优缺点

1.优点:解决了单继承的扩展性低缺点
2.缺点:没有返回值需要单独创建Thread线程类启动
3.使用非常简单的方式实现多线程

2.5.3、实现Callable接口创建FutureTask对象的优缺点

1.优点:带有返回值并且也是通过接口实现方式创建
2.缺点:使用麻烦并且接收返回值会造成程序阻塞(暂停)并且一个FutureTask对象只能启动一个线程

2.6.多线程注意事项

线程创建后不允许重复启动(不允许在同一时间启动两次)
以及run方法是由JVM虚拟机负责调用,如果我们自己调用则无法实现多线程的程序
start方法是启动线程并由JVM虚拟机负贡调用run方法实现多线程
多线程调用时是非常严谨的,一定不能出错,否则会影响程序的执行