程序.进程.线程
在操作系统中运行的==程序==就是==进程==,例如微信、IDE、QQ等(暴露年龄了🤐)
一个进程中可以存在多个线程,如播放一个视频时有声音、图像、文字等等
Process与Thread
- ==程序==是指令和数据的有序集合,其本身没有任何运行的含义,只是一个静态的概念。
- ==进程==是执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位。
- 通常在一个==进程==中可以包含若干个==线程==,一个进程中至少有一个线程(main),不然没有存在的意义。线程是CPU调度和执行的单位。
普通方法调用和多线程图解
线程的三种创建方式
- 继承Thread类(※※)
- 实现Runnable接口(※※※)
- 实现Callable接口(※)
一、继承Thread类的实现(※※)
实现步骤
如下,JDK帮助文档中关于==Thread==类的使用介绍
(PS:需要这个中文版JDK帮助文档的,可到网盘自行下载。网盘容易被墙,失效了可以评论让我分享)
网盘链接:https://pan.baidu.com/s/1tFMqo7B5Umd439nnU9LhyQ 提取码:s6c4
使用==继承Thread类==实现多线程,根据JDK帮助文档主要有以下三个步骤:
- 自定义一个类去继承==Thread类==。
- 重写==run()== 方法,编写线程执行体。
- 创建线程对象,调用==start()== 方法启动线程
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.thread;
public class TestThread1 extends Thread { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("run方法线程体----"+i); }
}
public static void main(String[] args) {
TestThread1 thread1 = new TestThread1(); thread1.start();
for (int i = 0; i < 1000; i++) { System.out.println("main方法,主线程----"+i); }
}
}
|
案例应用
多线程同步下载图片:利用多线程来下载网络图片。这里我使用的是apache的commons.io包中FileUtils文件工具类,使用FileUtils文件工具类下的==copyURLToFile方法==,接收一个url地址即可下载网络图片
注意: commons.io包需要去apache官网下载,然后将下载的jar包放入==lib目录(包)中==,并右击lib包,选择Add as Library将lib中的jar包添加到Library中
定义一个WebDownloader类作为下载器。如下,查看copyURLToFile()方法的源码实现可发现,copyURLToFile方法至少需要传入一个url地址参数,并且需要抛出一个IOException异常
在下载器中定义一个downloader方法,接收两个参数(url地址和文件名),并使用copyURLToFile来下载传入的地址中的文件
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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方法出现问题!"); } }
}
|
在TestThread2继承类中,定义两个私有属性,分别保存url地址和文件名,并用构造器给两个属性赋值。继承了==Thread==类,还需要在TestThread2类中重写run方法,在run方法中即可编写下载文件线程的执行体(调用下载器中的downloader方法下载文件)
然后,便可在main主线程中创建多个线程对象 ,调用对应的start()方法启动线程了~
(描述过于繁杂,也可以直接看代码中简洁的注释,我主要是想写博客时再加深一下自己的记忆🤣🤣)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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 Thread1 = new TestThread2("https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3173584241,3533290860&fm=26&gp=0.jpg","tupian1.jpg"); TestThread2 Thread2 = new TestThread2("https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg","tupian2.jpg"); TestThread2 Thread3 = new TestThread2("https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1208538952,1443328523&fm=26&gp=0.jpg","tupian3.jpg");
Thread1.start(); Thread2.start(); Thread3.start(); }
}
|
完整代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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
| package com.thread;
import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.IOException; import java.net.URL;
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 Thread1 = new TestThread2("https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3173584241,3533290860&fm=26&gp=0.jpg","tupian1.jpg"); TestThread2 Thread2 = new TestThread2("https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg","tupian2.jpg"); TestThread2 Thread3 = new TestThread2("https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1208538952,1443328523&fm=26&gp=0.jpg","tupian3.jpg");
Thread1.start(); Thread2.start(); Thread3.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方法出现问题!"); } }
}
|
二、实现Runnable接口(※※※)
如下,查看官方JDK帮助文档==实现Runnable接口==与==Thread==类的使用两种方式实现多线程差别不大
概括就是:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
代码实现
和继承Thread的差别只是==implements Runnable==和开启线程时需要丢入runnable接口的实现类 ==new Thread(testThread3).start();==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.thread;
public class TestThread3 implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("run方法线程体----"+i); }
}
public static void main(String[] args) { TestThread3 testThread3 = new TestThread3();
new Thread(testThread3).start();
for (int i = 0; i < 10; i++) { System.out.println("main方法,主线程----"+i); }
} }
|
案例应用
如下,使用”继承Thread类”的同一个案例,只是两个地方有所不同
两种方式实现多线程小结
继承Thread类:
- 子类继承Thread类具备多线程能力
- 启动线程:==子类对象 . start()==
- 不建议使用:避免OOP单继承的局限性
实现Runnable接口:
- 实现接口Runnable具有多线程能力
- 启动线程:**==传入目标对象+Thread对象 . start()==**
- 推荐使用:避免了单继承的局限性,方便同一个对象被多个线程使用
拓展案例
==多线程同时操作同一个对象==
案例: 买火车票的例子,一共有10张火车票,三个人(三个线程)同时去抢这10张火车票,输出这三个人分别抢了哪几张票?
思路: 创建一个线程对象,实现Runnable接口,重写了run方法(线程体中:总数10张票,每拿一张票就减减,小于1就跳出循环),三个线程都在用这10张票,每个人都会进去拿
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.thread;
public class TestThread4 implements Runnable {
private int ticketNums = 10;
@Override public void run() { while (true){ if (ticketNums<=0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票"); } }
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"小明").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛党").start(); }
}
|
运行结果
这时会有一个问题,不同的线程拿到了同一张票
发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
这是并发问题,后面再出博客讲解这个
三、实现Callable接口
实现步骤:
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(3);
- 提交执行:Future r1 = ser.submit(T1);
- 获取结果:Boolean rs1 = r1.get();
- 关闭服务:ser.shutdownNow();
实现Callable接口方法,用的很少不是重点,这里不在具体赘述了
案例应用
还是使用下载网络文件的案例,注意与==继承Thread类==和==实现Runnable接口==两种方式的区别即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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
| package com.thread.callable;
import org.apache.commons.io.FileUtils;
import java.io.File; import java.io.IOException; import java.net.URL; 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() { WebDown webDown = new WebDown(); webDown.downloader(url,name); System.out.println("下载了文件名为:"+name); return true; }
public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable T1 = new TestCallable("https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3173584241,3533290860&fm=26&gp=0.jpg","tupian1.jpg"); TestCallable T2 = new TestCallable("https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1906469856,4113625838&fm=26&gp=0.jpg","tupian2.jpg"); TestCallable T3 = new TestCallable("https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1208538952,1443328523&fm=26&gp=0.jpg","tupian3.jpg");
ExecutorService ser = Executors.newFixedThreadPool(3); Future<Boolean> r1 = ser.submit(T1); Future<Boolean> r2 = ser.submit(T2); Future<Boolean> r3 = ser.submit(T3);
Boolean rs1 = r1.get(); Boolean rs2 = r2.get(); Boolean rs3 = r3.get();
ser.shutdownNow();
}
}
class WebDown{ 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方法出现问题!"); } }
}
|
上期回顾
java中的接口定义与实现
关于博主
为了让结尾好看一点…..🙄🙄🙄
今天就到这了,已经快凌晨十二点了🥱,再不休息我担心明天早上,又会出现一股神秘的东方力量让我赖床了!