每日一记之多线程初识


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

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

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

注: 对于单核心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类

代码

java
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执行速度很快 让人觉得是一瞬间执行了多个任务实际上还是单个执行
多核则是真正的多线程在同一时间可以真正执行多个任务。
*/

控制台:

plaintext
我在看代码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转换为文件存储)

代码:

java
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方法出现异常");//异常提示

}
}

}


控制台:

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

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


方法二:实现Runnable接口

代码:

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

java
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方法创建多线程 避免单线程局限性、灵活方便、方便同一个对象被多个线程使用

控制台:

plaintext
我在看代码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

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


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

代码:

java
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毫秒求冠军

代码:

java
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();
}
}

控制台:

plaintext
乌龟--->跑了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接口

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

java
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();

}
}

控制台:

plaintext
我在学习多线程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-