1、进程和线程
-
进程:process
-
线程:thread
-
进程是执行程序的一次执行过程,动态的概念,是系统资源分配的单位
-
进程中可以包含多个线程,线程是CPU调度和执行的单位。
-
一个进程至少包含一个线程。
-
很多的多线程是模拟出来的。真正的多线程是指 多核,也就是多个CPU。
-
即使在一个CPU的情况下,在同一时间CPU只能执行一条代码,只是切换的时间很快,所以会有同时执行的错觉
-
对于同一份资源(临界资源),会存在抢夺的情况,需要加入并发控制
-
每个线程在自己的工作内存交互,内存控制不当会造成数据的不一致。
2、创建方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
3、Thread类
- 自定义线程类,继承Thread类
- 重写run()方法,编写执行代码
- 创建线程对象,调用start()方法启动线程
public class MyThreadOne extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run方法执行");
}
}
public static void main(String[] args) {
MyThreadOne myThreadOne = new MyThreadOne();
//run方法执行完之后,才会执行main方法
myThreadOne.run();
//start()和main()是交替执行的
myThreadOne.start();
for (int i = 0; i < 3000; i++) {
System.out.println("main方法执行");
}
}
}
部分输出结果
第273次main方法执行
第10次run方法执行
第274次main方法执行
第11次run方法执行
第275次main方法执行
第12次run方法执行
第276次main方法执行
第13次run方法执行
第277次main方法执行
第14次run方法执行
第278次main方法执行
第15次run方法执行
第279次main方法执行
第16次run方法执行
第280次main方法执行
例2 下载图片
//下载类
public class MyThreadTwo extends Thread {
private String url;
private String name;
public MyThreadTwo(String url,String name){
this.name = name;
this.url = url;
}
//重写run方法
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载图片"+name+"完毕");
}
//主方法
public static void main(String[] args) {
MyThreadTwo myThreadTwo1 = new MyThreadTwo("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605431864179&di=dfe29259d39d50b30bd78a2fd520e5df&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F30%2F29%2F01300000201438121627296084016.jpg","1.jpg");
MyThreadTwo myThreadTwo2 = new MyThreadTwo("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605432047363&di=72a55f655352297dbef4e2cedd851c49&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F55%2F22%2F20300000929429130630222900050.jpg","2.jpg");
MyThreadTwo myThreadTwo3 = new MyThreadTwo("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605432093023&di=d929c258e7d8ac88be9b900ef2e38a30&imgtype=0&src=http%3A%2F%2Fa2.att.hudong.com%2F06%2F02%2F19300534106437134465026151672.jpg","3.jpg");
myThreadTwo1.start();
myThreadTwo2.start();
myThreadTwo3.start();
}
}
//下载器
class WebDownloader{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
下载图片1.jpg完毕
下载图片3.jpg完毕
下载图片2.jpg完毕
4、Runnable
- 实现Runnable接口
- 重写run方法
- 执行线程需要丢入runnable接口的实现类
- 调用start()
public class MyThreadThree implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run方法执行");
}
}
public static void main(String[] args) {
//启动方式不一样
MyThreadThree myThreadThree = new MyThreadThree();
//丢入 runnable的实现类
new Thread(myThreadThree).start();
for (int i = 0; i < 3000; i++) {
System.out.println("main方法执行");
}
}
}
5、推荐使用
- 继承Thread 类 不建议使用。避免OOP单继承局限性
- 实现Runnable接口 推荐使用。灵活方便,一个对象可以被多个线程使用。
6、Callable
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:
- ExecutorService ser = Executor.newFixedThreadPool(1);
- 提交执行
- Futureresult1 = ser.submit(1);
- 获取结果
- boolean r1 = result1.get();
- 关闭服务
- ser.shutdownNow();
例子
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class MyThreadSix implements Callable<Boolean> {
private String url;
private String name;
public MyThreadSix(String url,String name){
this.name = name;
this.url = url;
}
//重写call方法
@Override
public Boolean call() {
WebDownload webDownload = new WebDownload();
webDownload.downloader(url,name);
System.out.println("下载图片"+name+"完毕");
return true;
}
//主方法
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThreadSix t1 = new MyThreadSix("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605431864179&di=dfe29259d39d50b30bd78a2fd520e5df&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F30%2F29%2F01300000201438121627296084016.jpg","1.jpg");
MyThreadSix t2 = new MyThreadSix("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605432047363&di=72a55f655352297dbef4e2cedd851c49&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F55%2F22%2F20300000929429130630222900050.jpg","2.jpg");
MyThreadSix t3 = new MyThreadSix("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605432093023&di=d929c258e7d8ac88be9b900ef2e38a30&imgtype=0&src=http%3A%2F%2Fa2.att.hudong.com%2F06%2F02%2F19300534106437134465026151672.jpg","3.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();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
// 关闭服务
ser.shutdownNow();
}
}
//下载器
class WebDownload{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
下载图片3.jpg完毕
下载图片1.jpg完毕
下载图片2.jpg完毕
true
true
true
- 好处
- 定义返回值
- 可以抛出异常