1、多线程概述
1、概念:多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理” 。
2、线程、进程、程序之间的关系:
程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
进程:进程是指执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位。
线程:线程是CPU调度的最小单位(程序执行流的最小单元),它被包含在进程之中,是进程中的实际运作单元。一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。
进程有独立的地址空间,线程没有单独的地址空间。
2、三种创建方式
2.1继承Thread类
创建线程的方式一:
继承Thread类,重写run()方法,调用start开启线程
package com.yuan.demo1;
/**
* @description:
* @author: ManolinCoder
* @time: 2022/2/28
*/
/*
* 创建线程的方式一:继承Thread类,重写run()方法,调用start开启线程
*
* 总结:一个电脑只有一个CPU,所以多个线程是同时执行,交替执行。
* 注意:线程开启不一定是立即执行,由cpu调度执行
* */
public class TestThread1 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("车库的A车"+i);
}
}
//main线程,主线程
public static void main(String[] args){
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程
testThread1.start();
for (int i = 0; i < 2000; i++) {
System.out.println("车库的s车"+i);
}
}
}
运行结果发现:
两个线程是交替重叠运行,也就是同时运行。
练习Thread 实现多线程同步下载图片:
package com.yuan.demo1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* @description:
* @author: ManolinCoder
* @time: 2022/2/28
*/
//练习Thread 实现多线程同步下载图片
public class TestThread2 extends Thread{
private String url;//网络图片地址
private String name; //保存的文件名
public TestThread2(String url,String name){
this.url=url;
this.name=name;
}
//下载图片线程的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://scpic3.chinaz.net/Files/pic/pic9/202202/apic38710_s.jpg","1.jpg");
TestThread2 t2 = new TestThread2("https://scpic3.chinaz.net/Files/pic/pic9/202202/apic38710_s.jpg","2.jpg");
TestThread2 t3 = new TestThread2("https://scpic3.chinaz.net/Files/pic/pic9/202202/apic38710_s.jpg","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
运行结果:
因为三个文件我找的都是同一张图,所以下载后就是三张一模一样的以上图片。
2.2实现Runnable接口(最常用)
创建线程的方式二:
实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
package com.yuan.demo1;
/**
* @description:
* @author: ManolinCoder
* @time: 2022/2/28
*/
/*
* 创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
* */
public class TestThread3 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("车库的A车"+i);
}
}
//main线程,主线程
public static void main(String[] args){
//创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过我们的线程对象来开启我们的线程,代理
//Thread thread = new Thread(testThread3);
//thread.start();
new Thread(testThread3).start();
for (int i = 0; i < 2000; i++) {
System.out.println("车库的s车"+i);
}
}
}
运行结果也是两个线程同时运行。
2.3实现Callable接口(不常用)
package com.yuan.demo1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//JUC并发编程
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url,String name){
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
//下载图片
WebDownloader2 webDownloader = new WebDownloader2();//下载器
webDownloader.downloader(url,name);//下载文件的方式
System.out.println("下载了图片-->"+name);
return true;
}
//启动线程
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://scpic3.chinaz.net/Files/pic/pic9/202202/apic38710_s.jpg","你好1.jpg");
TestCallable t2 = new TestCallable("https://scpic3.chinaz.net/Files/pic/pic9/202202/apic38710_s.jpg","你好2.jpg");
TestCallable t3 = new TestCallable("https://scpic3.chinaz.net/Files/pic/pic9/202202/apic38710_s.jpg","你好3.jpg");
//创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行:
Future<Boolean> result1 = ser.submit(t1);
Future<Boolean> result2 = ser.submit(t2);
Future<Boolean> result3 = ser.submit(t3);
//获取结果
boolean r1 = result1.get();
boolean r2 = result2.get();
boolean r3 = result3.get();
//判断线程是否顺利结束或者有异常
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
//关闭服务
ser.shutdownNow();
}
}
//下载图片
class WebDownloader2 {
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
//输出异常信息
System.out.println("downloader方法出现异常");
}
}
}
小结:
这三种方式,实现Runnable接口这个方式最常用。