每日一记之多线程初识


在这之前我们要搞清楚进程与线程之间的区别。

进程: 是程序执行的一次过程,或是正在运行的一个程序,是一个动态的过程,由它自身的产生,存在和消亡的过程。

线程: 进程可以进一步细化为线程,线程指的是一个进程的执行过程中,同时执行了多项任务 例如:到中午了 我一边吃着饭一边玩着手机一边在聊天, 我在这个进程中同时执行了三个任务(分别是 吃饭、玩手机、聊天)。

注: 对于单核心cpu来说 多线程并不是真正意义上的多线程 而是处理速度太快让你误以为实在同时处理任务。多核心cpu则能带来真正的多线程处理,随着当今科技的发展电脑的cpu也都是多核心的了

创建多线程的三个方法:

  • 继承Thread类 重写**void run()方法 创建对象(创建线程) 调用start()**方法启动线程 注: Thrad类实现了Runnable接口

  • 实现Runnable接口 实现void run()方法 创建对象,再创建一个Thread对象将刚刚创建的对象放入(创建线程),在使用刚刚创建的Thread对象调用**start()**方法启动线程

  • 实现Callable接口(泛型接口 需要返回值类型),重写call()方法,创建目标对象,创建执行服务ExecutorService ser = Executors.newFixedThreadPool(1) 创建线程池 池内线程个数 1,然后提交执行:Futue<?> result = set.submit(1), 获取结果 var r1 = result.get(), 最后要关闭线程现有的池以关闭服务:ser.shutdownNow();


方法一:继承Thread类

代码

package DEVIL.多线程;

/**
* @auther Devil(丁杨维)
* @create 2021-10-13-11:47
*/
/*
创建线程方式一: 继承Tread类,重写run方法,调用start方法启动线程
*/
public class E_01 extends Thread{//继承Tread类
@Override
//重写run方法
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在学习" + i);
}
}
//main线程 主线程
public static void main(String[] args) {
//创建一个线程对象
E_01 thread = new E_01();
//启动线程
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码"+i);
}
}
}
/*注: 线程开启不一定会立即执行 会根据cpu的安排调度执行
对于单核cpu多线程其实是虚拟多线程 实际上是cpu执行速度很快 让人觉得是一瞬间执行了多个任务实际上还是单个执行
多核则是真正的多线程在同一时间可以真正执行多个任务。
*/

控制台:

我在看代码0
我在学习0
我在看代码1
我在学习1
我在看代码2
我在学习2
我在看代码3
我在学习3
我在看代码4
我在学习4
我在看代码5
我在学习5
我在看代码6
我在学习6
我在看代码7
我在学习7
我在看代码8
我在学习8
我在看代码9
我在学习9
我在看代码10
我在学习10
我在看代码11
我在学习11
我在看代码12
我在学习12
我在看代码13
我在学习13
我在看代码14
我在学习14
我在看代码15
我在学习15
我在看代码16
我在学习16
我在看代码17
我在学习17
我在看代码18
我在学习18
我在看代码19
我在学习19
我在看代码20
我在看代码21
我在看代码22
我在看代码23
我在看代码24
我在看代码25
我在看代码26
我在看代码27
我在看代码28
我在看代码29
我在看代码30
我在看代码31
我在看代码32
我在看代码33
我在看代码34
我在看代码35
我在看代码36
我在看代码37
.
.
.
.
.//此处省略 因为创建的线程早已结束
.
我在看代码191
我在看代码192
我在看代码193
我在看代码194
我在看代码195
我在看代码196
我在看代码197
我在看代码198
我在看代码199

可以发现我们创建的线程与main线程(主线程)是同时进行的 对这就是多线程


练习一:用方法一实现多线程同时下载图片

图片可以在网上通过源码查找图片地址

下载器则使用了外部引入的包里的类 包为commonio.jar

使用了其中的FileUtils类的copyURLtoFile类方法(可以将URL转换为文件存储)

代码:

package DEVIL.多线程;

import org.apache.commons.io.FileUtils;//引入了一个类

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
* @auther Devil(丁杨维)
* @create 2021-10-13-12:28
*/
public class Tread1 extends Thread{//创建一个线程类 继承Thread类
private String URl;
private String name;
public Tread1(String URl, String name){//初始化 URL用与存储URL name用于存储文件名
this.name = name;
this.URl = URl;
}
@Override
public void run() {//Thread类run方法重载
webDownLoader webDownLoader = new webDownLoader();//创建一个下载器对象
webDownLoader.downLoader(this.URl,this.name);//传参
System.out.println("下载了文件名:"+name);//输出文件名 用于一会查看线程执行情况
}

public static void main(String[] args) {
//main线程 主线程
//创建三个线程对象
Tread1 tread1 = new Tread1("https://t12.baidu.com/it/u=3350456484,1876742898&fm=55&app=54&fmt=auto?w=1680&h=630", "1.jpg");
Tread1 tread2 = new Tread1("https://t10.baidu.com/it/u=2536294109,1654178484&fm=55&app=54&fmt=auto?w=1680&h=630", "2.jpg");
Tread1 tread3 = new Tread1("https://t10.baidu.com/it/u=2156539333,402435846&fm=55&app=54&fmt=auto?w=1120&h=420","3.jpg");
//对象调用start方法启动线程
tread1.start();
tread2.start();
tread3.start();
}
}
class webDownLoader {//定义一个下载器可以将URL转换为文件 用到了我们刚引入的类

public void downLoader(String URl, String name) {
try {
FileUtils.copyURLToFile(new URL(URl), new File(name));//类方法将URL转换为文件
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出现异常");//异常提示

}
}

}


控制台:

下载了文件名:3.jpg
下载了文件名:2.jpg
下载了文件名:1.jpg

可以看出如果不是多线程的情况下下载次序应该是1 2 3 但实际却是3 2 1


方法二:实现Runnable接口

代码:

使用接口可以打破单一继承的局限、灵活方便、方便同一个对象被多个线程使用

package DEVIL.多线程;

/**
* @auther Devil(丁杨维)
* @create 2021-10-13-16:49
*/
//方式二:实现接口Runnable具有多线程能力
public class E_02 implements Runnable{
@Override
//重写run方法
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在学习" + i);
}
}
//main线程 主线程
public static void main(String[] args) {
//创建一个线程对象
E_02 runnable = new E_02();
//启动线程:传入目标对象+Thread对象.start()
// Thread thread1 = new Thread(thread);
// thread1.start();
//启动线程
new Thread(runnable).start();
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码"+i);
}
}
}
//注: 推荐使用实现接口Runnable方法创建多线程 避免单线程局限性、灵活方便、方便同一个对象被多个线程使用

控制台:

我在看代码0
我在学习0
我在看代码1
我在学习1
我在看代码2
我在学习2
我在看代码3
我在学习3
我在看代码4
我在学习4
我在看代码5
我在学习5
我在看代码6
我在学习6
我在看代码7
我在学习7
我在看代码8
我在学习8
我在看代码9
我在学习9
我在看代码10
我在学习10
我在看代码11
我在学习11
我在看代码12
我在学习12
我在看代码13
我在学习13
我在看代码14
我在学习14
我在看代码15
我在学习15
我在看代码16
我在学习16
我在看代码17
我在学习17
我在看代码18
我在学习18
我在看代码19
我在学习19
我在看代码20
我在看代码21
我在看代码22
我在看代码23
我在看代码24
我在看代码25
我在看代码26
我在看代码27
我在看代码28
我在看代码29
我在看代码30
我在看代码31
我在看代码32
我在看代码33
我在看代码34
我在看代码35
我在看代码36
我在看代码37
.
.
.
.
.//此处省略 因为创建的线程早已结束
.
我在看代码191
我在看代码192
我在看代码193
我在看代码194
我在看代码195
我在看代码196
我在看代码197
我在看代码198
我在看代码199

输出结果与方法一实现的一致。


练习二:用方法二完成练习一

代码:

package DEVIL.多线程;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
* @auther Devil(丁杨维)
* @create 2021-10-13-17:01
*/
public class Runnable1 implements Runnable{
private String URl;
private String name;
public Runnable1(String URl, String name){//初始化 URL用与存储URL name用于存储文件名
this.name = name;
this.URl = URl;
}
@Override
public void run() {//Thread类run方法重载
webDownLoader webDownLoader = new webDownLoader();//创建一个下载器对象
webDownLoader.downLoader(this.URl,this.name);//传参
System.out.println("下载了文件名:"+name);//输出文件名 用于一会查看线程执行情况
}

public static void main(String[] args) {
//main线程 主线程
//创建三个线程对象
Runnable1 tread1 = new Runnable1("https://t12.baidu.com/it/u=3350456484,1876742898&fm=55&app=54&fmt=auto?w=1680&h=630", "1.jpg");
Runnable1 tread2 = new Runnable1("https://t10.baidu.com/it/u=2536294109,1654178484&fm=55&app=54&fmt=auto?w=1680&h=630", "2.jpg");
Runnable1 tread3 = new Runnable1("https://t10.baidu.com/it/u=2156539333,402435846&fm=55&app=54&fmt=auto?w=1120&h=420","3.jpg");
//对象调用start方法启动线程
new Thread(tread1).start();
new Thread(tread2).start();
new Thread(tread3).start();
}
}
class webDownLoader {//定义一个下载器可以将URL转换为文件 用到了我们刚引入的类

public void downLoader(String URl, String name) {
try {
FileUtils.copyURLToFile(new URL(URl), new File(name));//类方法将URL转换为文件
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出现异常");//异常提示

}
}

}


结果与练习一致


练习三:龟兔赛跑

此处使用了Thread类的类方法**sleep()**用于模拟延时

总路程100步 先到即为冠军 兔子每十部休息10毫秒求冠军

代码:

package DEVIL.多线程;

/**
* @auther Devil(丁杨维)
* @create 2021-10-13-17:24
*/
public class E_04 {
}
class Race implements Runnable{
private static String winner;
@Override
public void run() {
for(int i = 1; i<=100; i++){
//模拟兔子睡觉
if(Thread.currentThread().getName().equals("兔子")&& i%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean flag = gameOver(i);//判断比赛是否结束了
if(flag){//是跳出结束循环
break;
}
System.out.println(Thread.currentThread().getName()+"--->跑了"+i+"步了");
}
}

public boolean gameOver(int steps){
if(winner!=null){
return true;
}
else{
if(steps>=100){
winner = Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
}
return false;
}

public static void main(String[] args) {
Race race = new Race();

new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}

控制台:

乌龟--->跑了1步了
兔子--->跑了1步了
兔子--->跑了2步了
乌龟--->跑了2步了
乌龟--->跑了3步了
兔子--->跑了3步了
乌龟--->跑了4步了
乌龟--->跑了5步了
兔子--->跑了4步了
乌龟--->跑了6步了
乌龟--->跑了7步了
乌龟--->跑了8步了
乌龟--->跑了9步了
乌龟--->跑了10步了
乌龟--->跑了11步了
兔子--->跑了5步了
乌龟--->跑了12步了
兔子--->跑了6步了
乌龟--->跑了13步了
乌龟--->跑了14步了
乌龟--->跑了15步了
乌龟--->跑了16步了
兔子--->跑了7步了
乌龟--->跑了17步了
乌龟--->跑了18步了
乌龟--->跑了19步了
乌龟--->跑了20步了
乌龟--->跑了21步了
乌龟--->跑了22步了
乌龟--->跑了23步了
乌龟--->跑了24步了
乌龟--->跑了25步了
乌龟--->跑了26步了
兔子--->跑了8步了
乌龟--->跑了27步了
兔子--->跑了9步了
乌龟--->跑了28步了
乌龟--->跑了29步了
乌龟--->跑了30步了
乌龟--->跑了31步了
乌龟--->跑了32步了
乌龟--->跑了33步了
乌龟--->跑了34步了
乌龟--->跑了35步了
乌龟--->跑了36步了
乌龟--->跑了37步了
乌龟--->跑了38步了
乌龟--->跑了39步了
乌龟--->跑了40步了
乌龟--->跑了41步了
乌龟--->跑了42步了
乌龟--->跑了43步了
乌龟--->跑了44步了
乌龟--->跑了45步了
乌龟--->跑了46步了
乌龟--->跑了47步了
乌龟--->跑了48步了
乌龟--->跑了49步了
乌龟--->跑了50步了
乌龟--->跑了51步了
乌龟--->跑了52步了
乌龟--->跑了53步了
乌龟--->跑了54步了
乌龟--->跑了55步了
乌龟--->跑了56步了
乌龟--->跑了57步了
乌龟--->跑了58步了
乌龟--->跑了59步了
乌龟--->跑了60步了
乌龟--->跑了61步了
乌龟--->跑了62步了
乌龟--->跑了63步了
乌龟--->跑了64步了
乌龟--->跑了65步了
乌龟--->跑了66步了
乌龟--->跑了67步了
乌龟--->跑了68步了
乌龟--->跑了69步了
乌龟--->跑了70步了
乌龟--->跑了71步了
乌龟--->跑了72步了
乌龟--->跑了73步了
乌龟--->跑了74步了
乌龟--->跑了75步了
乌龟--->跑了76步了
乌龟--->跑了77步了
乌龟--->跑了78步了
乌龟--->跑了79步了
乌龟--->跑了80步了
乌龟--->跑了81步了
乌龟--->跑了82步了
乌龟--->跑了83步了
乌龟--->跑了84步了
乌龟--->跑了85步了
乌龟--->跑了86步了
乌龟--->跑了87步了
乌龟--->跑了88步了
乌龟--->跑了89步了
乌龟--->跑了90步了
乌龟--->跑了91步了
乌龟--->跑了92步了
乌龟--->跑了93步了
乌龟--->跑了94步了
乌龟--->跑了95步了
乌龟--->跑了96步了
乌龟--->跑了97步了
乌龟--->跑了98步了
乌龟--->跑了99步了
winner is 乌龟

方法三:实现Callable接口

关联到线程池 以后一定用到很多 现在初学阶段使用少

package DEVIL.多线程;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* @auther Devil(丁杨维)
* @create 2021-10-14-0:31
*/
public class E_05 {
}
//创建一个新的线程类 通过实现Callable接口
class Callable1 implements Callable<Boolean>{

@Override
public Boolean call() throws Exception {
for (int i=0; i<20; i++){
System.out.println("我在学习多线程"+i);
}
return true;
}

public static void main(String[] args)throws Exception {
//主线程
//创建目标对象
Callable1 callable1 = new Callable1();
//创建线程池(创建执行服务)
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> result = ser.submit(callable1);
//执行主线程
for (int i = 0; i < 100; i++) {
System.out.println("主线程"+i);
}
//获取结果
boolean r1 = result.get();
//关闭服务
ser.shutdownNow();

}
}

控制台:

我在学习多线程0
我在学习多线程1
主线程0
主线程1
主线程2
主线程3
主线程4
我在学习多线程2
主线程5
主线程6
主线程7
我在学习多线程3
主线程8
主线程9
我在学习多线程4
主线程10
主线程11
主线程12
我在学习多线程5
我在学习多线程6
主线程13
主线程14
我在学习多线程7
主线程15
主线程16
主线程17
主线程18
主线程19
我在学习多线程8
主线程20
主线程21
主线程22
我在学习多线程9
主线程23
主线程24
主线程25
我在学习多线程10
主线程26
主线程27
主线程28
我在学习多线程11
主线程29
主线程30
主线程31
主线程32
主线程33
我在学习多线程12
主线程34
主线程35
我在学习多线程13
主线程36
主线程37
主线程38
我在学习多线程14
主线程39
我在学习多线程15
主线程40
我在学习多线程16
主线程41
我在学习多线程17
主线程42
我在学习多线程18
主线程43
我在学习多线程19
主线程44
主线程45
主线程46
主线程47
主线程48
主线程49
主线程50
主线程51
主线程52
主线程53
主线程54
主线程55
主线程56
主线程57
主线程58
主线程59
主线程60
主线程61
主线程62
主线程63
主线程64
主线程65
主线程66
主线程67
主线程68
主线程69
主线程70
主线程71
主线程72
主线程73
主线程74
主线程75
主线程76
主线程77
主线程78
主线程79
主线程80
主线程81
主线程82
主线程83
主线程84
主线程85
主线程86
主线程87
主线程88
主线程89
主线程90
主线程91
主线程92
主线程93
主线程94
主线程95
主线程96
主线程97
主线程98
主线程99

符合多线程执行

-end-