多任务
什么是多任务,比如边吃饭边玩手机,边开车边打电话,一个人同时干几件事,这就是多任务
进程
计算机同时执行多个进程,一般是同时运行多个软件
线程
使得在一个程序内部能拥有多个线程并行执行
多线程
同方向多条路供车行驶,厕所设立多个坑位,不会造成拥堵这就是多线程
Process与Thread
说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。
核心理念
线程就是独立的执行路径;
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
main()称之为主线程,为系统的入口,用于执行整个程序;
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
线程会带来额外的开销,如cpu调度时间,并发控制开销。
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程创建
Thread 、Runnable 、Callable
Thread class:继承Thread 类(重点)
Runnable接口:实现Runnable接口(重点)
Callable接口:实现Callable接口(了解)
Thread
自定义线程类继承Thread类(养成习惯:继承完Thread类就重写run方法)
重写run()方法,编写执行代码
创建线程对象,调用start()方法启动线程
public class test extends Thread{
//重写run方法
//快捷重写:打run就会提示
@Override
public void run() {
//线程体
for (int i = 0; i < 10; i++) {
System.out.println("我是第"+ i +"个");
}
// super.run();
}
//mian方法主线程
public static void main(String[] args) {
//创建线程对象
test test = new test();
//启动线程
test.start();
}
}
图片下载
注意提前导入需要的包:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.2</version> </dependency>
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Thread实现多线程同步下载图片
public class test extends Thread{
//储存url
private String url;
//储存文件名
private String name;
public test(String url, String name){
//把传进来的url赋值给private String url
this.url = url;
//把传进来的name赋值给private String name
this.name = name;
}
//线程执行方法
@Override
public void run() {
//创建内部类
webdownload webdownload = new webdownload();
//调用内部类中的方法,把url和name传进去
webdownload.download(url,name);
}
//创建主线程
public static void main(String[] args) {
//把url和name传进test方法中,再由test方法中的this赋值给定义的属性
test test1 = new test("https://img1.baidu.com/it/u=3217543765,3223180824&fm=253&fmt=auto&app=120&f=JPEG?w=1200&h=750", "hua.jpg");
test test2 = new test("https://img2.baidu.com/it/u=436635185,1433075744&fm=253&fmt=auto&app=138&f=JPEG?w=666&h=500", "shan.jpg");
test test3 = new test("https://img0.baidu.com/it/u=857510153,4267238650&fm=253&fmt=auto&app=120&f=JPEG?w=1200&h=675", "mao.jpg");
//开启线程
test1.start();
test2.start();
test3.start();
}
}
//内部类
class webdownload{
//下载方法,需要一个地址(url)和一个名字(name)
public void download(String url,String name){
//捕获异常
try {
//使用FileUtils.copyURLToFile方法下载图片
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
//报错提示
System.out.println("download异常");
}
}
}
实现Runable接口
//实现接口
public class test implements Runnable{
//重写方法
@Override
public void run() {
//简单循环
for (int i = 0; i < 10; i++) {
System.out.println("Runnable" + i);
}
}
//主线程
public static void main(String[] args) {
//new一个test
test test = new test();
//new一个线程,把test里的run方法放进去
Thread thread = new Thread(test);
//启动线程
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("main" + i);
}
}
}
并发问题
例子1:龟兔赛跑
public class test2 implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; 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+"步");
}
}
private boolean gameOver(int a ){
if (winner != null){
return true;
}{
if (a >= 100){
winner = Thread.currentThread().getName();
System.out.println("胜利者"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
test2 test2 = new test2();
new Thread(test2,"乌龟").start();
new Thread(test2,"兔子").start();
}
}