1.什么是进程?
1.1进程就是执行程序的一次执行过程,他是应该动态的概念,是系统分资源分配的单位。
2.什么是线程
2.1一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPI调度和执行的单位。
3.创建线程的方式
3.1继承Thread类,重写run()方法,调用start开启线(不建议使用,避免OPP的单继承局限性)案例
3.1.1创建一个maven项目 导入依赖
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
3.1.2 准备一个文件下载工具类(来自刚刚导入的依赖)
package com.sj; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; /** * 文件下载工具类 * @author SheJun */ public class DownLoader { public void downLoader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { System.out.println("io异常"); e.printStackTrace(); } } }
3.1.3写一个类继承Thread类,重写run()方法,调用start开启线(),测试
package com.sj;
/**
* @Author sj
* @Date: 2022/04/04/ 22:28
* @Description
*/
public class ThreadTest extends Thread {
/**
* 文件路径
*/
private String url;
/**
* 文件名
*/
private String name;
/**
* 构造器注入
* @param url
* @param name
*/
public ThreadTest(String url,String name){
this.url =url;
this.name = name;
}
/**
* 覆写run方法
*/
@Override
public void run() {
DownLoader downLoader = new DownLoader();
downLoader.downLoader(url,name);
System.out.println("下载的文件的名字:"+name);
}
/**
* 写一个主线程
* @param args
*/
public static void main(String[] args) {
ThreadTest threadTest1 = new ThreadTest("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/09/08/kuangstudya9f1a304-0940-45b7-9e6d-f1d4b46a155c.jpg","1.jpg");
ThreadTest threadTest2 = new ThreadTest(" https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudyb62b0ccb-55b5-4572-b067-347314beac15.jpg","2.jpg");
ThreadTest threadTest3 = new ThreadTest("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudy69ec9992-bed7-4eec-a550-5fe2aeb44737.jpg","3.jpg");
threadTest1.start();
threadTest2.start();
threadTest3.start();
}
}
我们预想的先下载1,然后2,3 但是这里并没有去这样去执行,所以说,当线程开启时,不是马上去执行的,需要我们的cpu调度去执行,谁先得到就先执行!
3.2.实现Runable接口,覆写run方法,传入目标对象到Thread类,调用start启动(建议使用,避免了单继承的局限性)
3.2.1 修改上面代码 如下
package com.sj;
/**
* @Author sj
* @Date: 2022/04/04/ 22:28
* @Description
*/
public class RunnableTest implements Runnable {
/**
* 文件路径
*/
private String url;
/**
* 文件名
*/
private String name;
/**
* 构造器注入
* @param url
* @param name
*/
public RunnableTest(String url, String name){
this.url =url;
this.name = name;
}
/**
* 覆写run方法
*/
@Override
public void run() {
DownLoader downLoader = new DownLoader();
downLoader.downLoader(url,name);
System.out.println("下载的文件的名字:"+name);
}
/**
* 写一个主线程
* @param args
*/
public static void main(String[] args) {
RunnableTest threadTest1 = new RunnableTest("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/09/08/kuangstudya9f1a304-0940-45b7-9e6d-f1d4b46a155c.jpg","1.jpg");
RunnableTest threadTest2 = new RunnableTest(" https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudyb62b0ccb-55b5-4572-b067-347314beac15.jpg","2.jpg");
RunnableTest threadTest3 = new RunnableTest("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudy69ec9992-bed7-4eec-a550-5fe2aeb44737.jpg","3.jpg");
new Thread(threadTest1).start();
new Thread(threadTest2).start();
new Thread(threadTest3).start();
}
}
测试,成功
3.3实现Callable接口,重写call方法,抛出异常,创建目标对象,创建执行服务,提交执行,关闭服务(案例)
3.3.1代码如下图
package com.sj;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static java.util.concurrent.Executors.*;
/**
* @Author sj
* @Date: 2022/04/04/ 22:28
* @Description
*/
public class CallableTest3 implements Callable<Boolean> {
/**
* 文件路径
*/
private String url;
/**
* 文件名
*/
private String name;
/**
* 构造器注入
* @param url
* @param name
*/
public CallableTest3(String url, String name){
this.url =url;
this.name = name;
}
public static void main(String[] args) {
CallableTest3 threadTest1 = new CallableTest3("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/09/08/kuangstudya9f1a304-0940-45b7-9e6d-f1d4b46a155c.jpg","1.jpg");
CallableTest3 threadTest2 = new CallableTest3(" https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudyb62b0ccb-55b5-4572-b067-347314beac15.jpg","2.jpg");
CallableTest3 threadTest3 = new CallableTest3("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudy69ec9992-bed7-4eec-a550-5fe2aeb44737.jpg","3.jpg");
// 创建执行服务
ExecutorService executorService = newFixedThreadPool(3);
// 提交执行
executorService.submit(threadTest1);
executorService.submit(threadTest2);
executorService.submit(threadTest3);
// 关闭服务
executorService.shutdownNow();
}
@Override
public Boolean call() throws Exception {
DownLoader downLoader = new DownLoader();
downLoader.downLoader(url,name);
System.out.println("下载的文件的名字:"+name);
return true;
}
}
由效果图我们可以出Callable也可以创建线程,(嘿嘿其实在这里我们如果点进源码去看这里其实用到了代理模式,下一遍我将告诉大家怎么去实现静态代理和jdk动态代理!)
4. 多线程操作多一个对象(初识并发问题,抢票案例)
4.1代码如下,在里面我们实现了Runnable接口,然后模拟抢票
package com.sj;
/**
* @Author sj
* @Date: 2022/04/04/ 22:28
* @Description
*/
public class RunnableTest2 implements Runnable {
/**
* 票数
*/
private int numbers= 10;
@Override
public void run() {
while (true){
if (numbers<=0){
break;
}
// 模拟抢票延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+numbers--+"票");
}
}
public static void main(String[] args) {
RunnableTest2 runnable1 = new RunnableTest2();
new Thread(runnable1,"黄牛").start();
new Thread(runnable1,"小红").start();
new Thread(runnable1,"薛之谦").start();
}
}
测试结果我们可以看出,当我们多个线程去操作一个资源的时候,会出现了线程不安全,数据混乱的情况!(初始并发问题)!
希望可以帮助到小伙伴们