多线程
线程简介
-
程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
-
进程:执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位。
-
线程:一个进程可以包含若干个线程,至少包含一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
注意:很多 多线程 是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的服务器,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,所以有同时执行的错局。
一些核心概念:
-
线程就是独立的执行路径;
-
在程序执行时,即使没有自己创建线程,后台也会有多个线程,如主线程(main()线程,用户线程)、gc线程(垃圾回收,守护线程);
-
main()被成为主线程,为系统的入口,用来执行整个程序;
-
在一个进程中,如果开辟了多个线程,线程的执行由调度器安排调度,调度器是与操作系统密切相关的,先后顺序是不能认为被干预的;
-
对同一份资源操作时,回存在资源抢夺的问题,需要加入并发控制;
-
线程会带来额外的开销,如cpu调度时间,并发控制开销;
-
每个线程在自己的工作内存交互,内存控制不当,会造成数据不一致
线程创建(三种方式)
继承Thread类(重点)
-
自定义线程类继承Thread类
-
重写run()方法**,编写线程执行体
-
创建线程对象,调用start()方法启动线程
-
package com.threads;
//创建方式一:继承Thread类,重写run方法,调用start方法
public class TestThread extends Thread {
public static void main(String[] args) {
//创建线程对象
TestThread testThread = new TestThread();
testThread.start();
for (int i = 0; i < 200; i++) {
System.out.println("我很好");
}
}
案例:下载网络图片
package com.threads;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThreads02{
public static void main(String[] args) {
Threads02 threads01 = new Threads02("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "bd.png");
Threads02 threads02 = new Threads02("https://img-home.csdnimg.cn/images/20201124032511.png", "csdn.png");
Threads02 threads03 = new Threads02("https://i0.hdslb.com/bfs/archive/ffc9365c13d04c7b1e5dc25ab1346ca00b02a147.png", "bilibili.png");
threads01.start();
threads02.start();
threads03.start();
}
}
class Threads02 extends Thread{
private String url;
private String name;
public Threads02(String url,String name){
this.url=url;
this.name=name;
}
注意:线程不一定立即执行,cpu会安排调度
实现Runnable接口(重点)
-
定义MyRunnable类实现Runnable接口
-
实现run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程 (启动线程:传入目标对象+Thread对象.start() )
-
package com.threads;
public class TestThreads03 implements Runnable {
推荐使用 实现Runnable接口的方法,可以避免了单继承的局限性,灵活方便,方便同一个对象被多个线程使用
实例:实现Runnable接口 方便同一个对象被多个线程使用
package com.threads;
//多个线程同时操作同一个对象
//买火车票的例子
//最后根据输出结果发现问题:多个线程操作同一个资源时,线程不安全,出现数据紊乱
public class TestThreads04 implements Runnable {
private int ticket=10;
龟兔赛跑:
-
首先来个赛道距离。然后要距离终点越来越近
-
判断比赛是否结束
-
打印出胜利者
-
龟兔赛跑开始
-
故事中乌龟是赢的,兔子要睡觉,所以我们模拟兔子睡觉
-
终于,乌龟取得胜利
package com.threads;
/*
龟兔赛跑:
1. 首先来个赛道距离。然后要距离终点越来越近
2. 判断比赛是否结束
3. 打印出胜利者
4. 龟兔赛跑开始
5. 故事中乌龟是赢的,兔子要睡觉,所以我们模拟兔子睡觉
6. 终于,乌龟取得胜利
*/
public class TestRace {
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
class Race implements Runnable {
private int track = 50; //赛道50米
private String winner;
实现Callable接口(了解)
-
实现Callable接口,需要返回值类型
-
重写call方法,需要抛出异常
-
创建目标对象
-
创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);
-
提交执行:Future<Boolean> result1=ser.submit(t1);
-
获取结果:boolean r1=result1.get()
-
关闭服务:ser.shutdownNow();
演示:利用callable下载图片案例
package com.threads;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestThreads05 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Threads05 t1 = new Threads05("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "bd.png");
Threads05 t2 = new Threads05("https://img-home.csdnimg.cn/images/20201124032511.png", "csdn.png");
Threads05 t3 = new Threads05("https://i0.hdslb.com/bfs/archive/ffc9365c13d04c7b1e5dc25ab1346ca00b02a147.png", "bilibili.png");
//3. 创建执行服务:
ExecutorService ser= Executors.newFixedThreadPool(3);
//4. 提交执行:
Future<Boolean> result1=ser.submit(t1);
Future<Boolean> result2=ser.submit(t2);
Future<Boolean> result3=ser.submit(t3);
//5. 获取结果: 需要抛出异常
boolean r1=result1.get();
//6. 关闭服务:
ser.shutdownNow();
}
}
class Threads05 implements Callable<Boolean>{
private String url;
private String name;
//构造器
public Threads05(String url, String name) {
this.url=url;
this.name=name;
}