Thread
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
- 线程不一定立即执行,CPU安排调度
多线程图片下载
package Thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
// 练习 Thread, 实现多线程同步下载
public class TestThread2 extends Thread{
private String url; // 网络图片地址
private String name; // 保存的文件名
public TestThread2(String url, String name){
this.url = url;
this.name = name;
}
@Override
public void run(){
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2Fv2-a4bd0e19fc987be19e1133fa61016226_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671248921&t=1603183c02e86f4115c0207a4c6611fe", "pic1.jpg");
TestThread2 t2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2Fv2-a4bd0e19fc987be19e1133fa61016226_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671248921&t=1603183c02e86f4115c0207a4c6611fe", "pic2.jpg");
TestThread2 t3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2Fv2-a4bd0e19fc987be19e1133fa61016226_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671248921&t=1603183c02e86f4115c0207a4c6611fe", "pic3.jpg");
t1.start();
t2.start();
t3.start();
}
}
// 下载器
class WebDownloader {
// 下载方法
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题!");
}
}
}
继承Tread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用: 避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
package Thread;
public class TestThread3 implements Runnable {
@Override
public void run() {
// run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
// 创建一个线程对象
TestThread3 thread3 = new TestThread3();
//创建线程对象,通过线程对象来开启我们的线程,代理
// 调用start()方法开启线程
new Thread(thread3).start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}
线程创建方式3:实现callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行任务:
ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行
Future result1 = ser.submit(t1);
- 获取结果
boolean r1 = result.get()
- 关闭服务
ser.shutdownNow();
package Thread.Callable;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
// 线程创建方式3:实现callable接口
/*
callable的好处:
1. 可以定义返回值
2. 可以抛出异常
*/
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url, String name){
this.name = name;
this.url = url;
}
@Override
public Boolean call(){
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2Fv2-a4bd0e19fc987be19e1133fa61016226_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671248921&t=1603183c02e86f4115c0207a4c6611fe", "pic1.jpg");
TestCallable t2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2Fv2-a4bd0e19fc987be19e1133fa61016226_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671248921&t=1603183c02e86f4115c0207a4c6611fe", "pic2.jpg");
TestCallable t3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2Fv2-a4bd0e19fc987be19e1133fa61016226_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671248921&t=1603183c02e86f4115c0207a4c6611fe", "pic3.jpg");
// 1. 创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(3);
// 2. 提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
// 3. 获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
// 关闭服务
ser.shutdown();
}
}
// 下载器
class WebDownloader {
// 下载方法
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题!");
}
}
}
callable的好处:
- 可以定义返回值
- 可以抛出异常
Lambda 表达式
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口;
public interface Runnable{
public abstract void run();
}
- 对于函数式接口,可以通过lambda表达式来创建该接口的对象。
package Lambda;
public class TestLambda {
// 3. 静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
// 4 局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda3");
}
}
like = new Like3();
like.lambda();
// 5 匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
// 6. 用lambda简化
like = ()-> {
System.out.println("i like lambda4");
};
like.lambda();
}
}
// 1 实现一个函数式接口
interface ILike{
void lambda();
}
// 2 实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
测试线程stop
- 建议线程正常停止—>利用次数,不建议死循环;
- 建议使用标志位—>设置一个标志位;
- 不要使用stop或者destroy等过时或者JDK不建议使用的方法。
sleep
- 模拟延时;
- 倒计时,打印系统时间
yield
- 测试礼让线程
- 礼让不一定成功,看cpu心情
join插队
package Thread;
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程vip来啦~");
}
}
public static void main(String[] args) throws InterruptedException {
// 启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
// 主线程
for (int i = 0; i < 1000; i++) {
if (i==200){
thread.join();
}
System.out.println("main+"+i);
}
}
}
设置优先级
t.setPriority(); //0-10,默认为5
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等
死锁
产生死锁的四个必要条件
- 互斥条件
- 请求与保持条件
- 不可剥夺条件
- 循环等待条件
package Thread;
// 死锁:多个线程相互抱着对方需要的资源,然后形成僵持。
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "灰姑凉");
Makeup g2 = new Makeup(1, "白雪公主");
g1.start();
g2.start();
}
}
class Lipstick{
}
// 镜子
class Mirror{
}
class Makeup extends Thread{
// 需要的资源只有一份, 用static来保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 选择
String girlName; // 使用化妆品的人
public Makeup(int choice, String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 化妆,互相持有对方的资源
private void makeup() throws InterruptedException {
// if (choice==0){
// synchronized (lipstick){ // 获得口红的锁
// System.out.println(this.girlName+"获得口红的锁");
// Thread.sleep(1000);
// synchronized (mirror){ // 一秒后想获得镜子
// System.out.println(this.girlName+"获得镜子的锁");
// }
// }
// }else{
// synchronized (mirror){ // 获得镜子的锁
// System.out.println(this.girlName+"获得镜子的锁");
// Thread.sleep(2000);
// synchronized (lipstick){ // 一秒后想获得口红
// System.out.println(this.girlName+"获得口红的锁");
// }
// }
// }
if (choice==0){
synchronized (lipstick){ // 获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){ // 一秒后想获得镜子
System.out.println(this.girlName+"获得镜子的锁");
}
}else{
synchronized (mirror){ // 获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick){ // 一秒后想获得口红
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
ReentrantLock 可重入锁
作用范围为代码块
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
try{
lock.lock();
xxxx
}finally{
lock.unlock();
}
线程池
背景:经常创建和销毁,使用特别大的资源,比如并发情况下的线程,对象能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用。
优势:
- 提高响应速度
- 降低资源消耗
- 便于线程管理
package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 测试线程池
public class TestPool {
public static void main(String[] args) {
// 1. 创建服务,创建线程池
// newFixedThreadPool 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2. 关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}