## 01_多线程概述
进程:应用程序(可执行文件)
一个进程可以有多个线程
线程: 进程的执行路径\单元
单线程:进程只有一个可执行路径,按照步骤一步一步执行,前面的步骤没有执行完,不会执行后面的步骤
多线程:进程有多个可执行路径
例子:
洗水壶 接水 烧水 洗茶壶 洗茶杯 拿茶叶 喝水
## 02_多线程并行和并发的区别:
多线程并行:多条线程同时执行,需要多核cpu
多线程并发:多条线程同时请求cpu,但是交替执行,由于cpu切换速度非常快,所以感觉像多线程并行
## 03_多线程的第一种实现方式
1.创建一个类继承Thread类
2.重写Thread类里面的run()方法,把多线程要执行的代码放入run()方法里面
3.创建该类的对象
4.调用start()方法启动线程
## 04_多线程的第二种实现方式
/*
1.创建一个类实现Runnable接口
2.重写run()方法,把多线程要执行的代码放入到run()方法里面
3.创建一个Thread对象,把实现Runnable接口类的对象传入进来
4.调用start()方法启动线程
*/
第三种实现方式: 通过匿名内部类
Thread th = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ;i<200;i++) {
System.out.println("i...这是第"+i+"次循环");
}
}
});
th.start();
## 05_多线程两种实现方式之间的区别
* 继承Thread类:
* 好处: 代码相对比较简单. 因为是直接继承Thread类, 所以可以直接使用Thread类中的方法.
* 弊端: 扩展性差. 因为Java中类与类之间的继承只支持单继承, 不支持多继承. 类继承了Thread类,就不能在继承别的类了.
* 实现Runnable接口:
* 好处: 扩展性强.
* 弊端: 代码相对比较繁琐.
## 06_多线程常用的方法
currentThread(): 获取当前线程
setName() 修改线程的名字
getName() 获取线程的名字
start() 启动线程
sleep() 暂停线程执行
## 07_多线程同步代码块
// 多条线程同时请求一段相同的代码的时候,就会出现数据不安全的情况
// 怎么解决? 加锁 同步
// 同步代码块: 有一段代码需要加锁,不希望别人打扰
// 同步方法:当一个方法里面 的所有代码都需要加锁的时候
/*
同步代码块的锁对象:
1.锁对象可以是任意类型的对象
2.锁对象是唯一的
3.类的字节码文件对象 只有一个
*/
@Override
public void run() {
synchronized (MyThread.class) {
System.out.println(getName()+":打开厕所门");
System.out.println(getName()+":关闭厕所门");
System.out.println(getName()+":脱裤子");
System.out.println(getName()+":蹲下");
System.out.println(getName()+":用力...");
System.out.println(getName()+":擦屁股");
System.out.println(getName()+":穿裤子");
System.out.println(getName()+":冲厕所");
System.out.println(getName()+":打开厕所门");
System.out.println(getName()+":关闭厕所门");
System.out.println(getName()+":洗手,高高兴兴的走了");
}
}
## 08 同步方法
同步方法: 当整个方法里面的代码都需要加锁的时候
非静态同步方法:
锁对象: this
静态同步方法:
锁对象:该类.class
## 09 沏茶案例
public void 烧水() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println("水烧好了");
}
}).start();
}
## 10_ 多线程卖票的案例
public static int tickets = 100;
@Override
public void run() {
// 因为不知道每个窗口要卖多少张票,所以用循环
while(true) {// 线程1 1 线程2 0
synchronized (MyThread.class) {
if(tickets < 1) {//线程1 1 线程2 1 线程3 1 线程4 1
break;
}
try {
Thread.sleep(100);// 线程1 睡了 线程2 睡了 线程3 睡了 线程4 睡了
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
// 卖票的过程 线程1 醒了 线程2 醒了 线程3醒了 线程4 醒了
System.out.println(getName()+"...这是第"+tickets+"票");// 线程1 1 线程2 0 线程3 -1 线程4 -2
tickets--;
}
}
}
## 11 单线程下载器下载服务器上的文件
/**
* 单线程下载服务器文件
* @param srcPath
* @param destPath
*/
public static void singleThreadDownLoadFile(String srcPath,String destPath) {
/*
* 服务器下载文件
*/
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL(srcPath);
// 根据url对象得到字节输入流对象
is = url.openStream();
// 创建一个字节输出流对象
fos = new FileOutputStream(destPath);
// 定义一个变量,用来存储读取到的字节数据
int len = 0;
// 循环读取
while ((len = is.read()) != -1) {
// 写入数据
fos.write(len);
}
} catch (Exception e) {
System.out.println("读取失败,出现了异常");
}finally {
// 关闭流
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
System.out.println("关闭输入流失败");
}finally {
try {
fos.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
System.out.println("关闭输出流失败");
}
}
}
System.out.println("搞定");
}
## 12_ 单线程分段下载器下载服务器上的文件
/**
* 单线程指定字节下载服务器文件
* @param srcPath
* @param destPath
* @param start 起始字节
* @param end 结束字节
*/
public static void singleThreadDownLoadPartFile(String srcPath,String destPath,int start,int end) {
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL(srcPath);
//得到连接对象 设置请求属性
URLConnection uc = url.openConnection();
uc.setRequestProperty("Range", "bytes="+start+"-"+end);
// 通过uc连接对象 ,可以得到字节输入流对象
is = uc.getInputStream();
// 创建输出流对象
fos = new FileOutputStream(destPath);
// 定义一个变量用来存储读取到的字节数据
int len = 0;
// 循环读取
while((len = is.read()) != -1) {
// 写入文件
fos.write(len);
}
} catch (IOException e) {
System.out.println("出现了异常");
}finally {
// 关闭流
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
System.out.println("关闭输入流失败");
}finally {
try {
if(fos != null) {
fos.close();
}
} catch (IOException e) {
System.out.println("关闭输出流失败");
}
}
}
}
}
## 13_合并文件
public static void mergeFile(String srcPath,String destPath) {
File file = new File(srcPath);
File[] listFiles = file.listFiles();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(destPath);
if(listFiles != null) {
for(File file2 : listFiles) {
if(file2.isFile() && file2.getName().endsWith(".temp")) {
// IO流的代码
FileInputStream fis = new FileInputStream(file2);
int len = 0;
while((len = fis.read()) != -1) {
fos.write(len);
}
fis.close();
}
}
}
} catch (IOException e) {
System.out.println("出现了异常");
}finally {
try {
if(fos != null) {
fos.close();
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
## 14_多线程分段下载服务器文件
package com.itheima.多线程分段下载文件;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class MyThread extends Thread{
public String srcPath;
public int start;
public int end;
public String destPath;
public MyThread(String srcPath, int start, int end, String destPath) {
super();
this.srcPath = srcPath;
this.start = start;
this.end = end;
this.destPath = destPath;
}
@Override
public void run() {
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL(srcPath);
//得到连接对象 设置请求属性
URLConnection uc = url.openConnection();
uc.setRequestProperty("Range", "bytes="+start+"-"+end);
// 通过uc连接对象 ,可以得到字节输入流对象
is = uc.getInputStream();
// 创建输出流对象
fos = new FileOutputStream(destPath);
// 定义一个变量用来存储读取到的字节数据
int len = 0;
// 循环读取
while((len = is.read()) != -1) {
// 写入文件
fos.write(len);
}
} catch (IOException e) {
System.out.println("出现了异常");
}finally {
// 关闭流
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
System.out.println("关闭输入流失败");
}finally {
try {
if(fos != null) {
fos.close();
}
} catch (IOException e) {
System.out.println("关闭输出流失败");
}
}
}
}
}
## 15_获取服务器文件大小
public static int getFileSize(String path) {
int len = 0;
try {
URL url = new URL(path);
URLConnection uc = url.openConnection();
len = uc.getContentLength();
} catch (Exception e) {
System.out.println("计算大小出现异常");
}
return len;
}