java多线程基础
第一种方法:Thread
-
自定义线程类继承Thread类
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
-
注意:线程不一定立即执行,由CPU安排调度
继承Thread类创建多线程
package com.waves.dxcdemo; import java.text.DateFormat; import java.time.format.DateTimeFormatter; import java.util.Date; //创建多线程方式一:继承Thread类,重写run()方法,调用start开启线程 //总结 线程开启子线程不一定立即执行 ,由cpu调度执行 public class TestThread1 extends Thread{ @Override public void run() { //线程方法体 for (int i = 0; i < 20; i++) { System.out.println("2021-11-28 18:22 多线程代码"+i); } } public static void main(String[] args) { //main方式,主线程 TestThread1 thread1 = new TestThread1(); thread1.start(); //调用start方法,run 与 下方学习代码交替执行 // thread1.run(); //调用run方法,优先执行run方法体的业务代码 // Date date = new Date(); // long time = date.getTime(); for (int i = 0; i < 20; i++) { System.out.println("2021-11-28 18:22 学习多线程代码"+ i); } } }
继承Thread类使用多线程同步下载图片
package com.waves.dxcdemo; 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() { WedDownLoader wedDownLoader = new WedDownLoader(); wedDownLoader.downLoader(url,name); //图片保存在根目录 System.out.println("下载图片路径为"+url+"\n"+"下载的图片名字为"+name); } public static void main(String[] args) { TestThread2 xiancheng1 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fd.zdqx.com%2Fshirt_20190710%2F014.jpg&refer=http%3A%2F%2Fd.zdqx.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641202820&t=637cea2d77c04500bdbef46abce0e64e","动漫桌面1.jpg"); TestThread2 xiancheng2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fba794af4e3c6a4cf93b25f19e148dc4288e58b91.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203074&t=0b47d29f43a86fc5a2adae8bb5b60748","动漫桌面2.jpg"); TestThread2 xiancheng3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fc099d5c83bdffb17eff062d5298eb136f558b8fd.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203131&t=490c5ea40c0745215a8140d8118f22ec","动漫桌面3.jpg"); //三个线程,同时按照从上往下的顺序执行 同时线程也不是立即执行,看CPU安排调度 xiancheng1.start(); xiancheng2.start(); xiancheng3.start(); } } //下载器 class WedDownLoader{ 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下载异常"); } } }
第二种:实现Runable接口
-
定义实现MyRunnable类实现Runnable接口
-
实现Run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
-
注意:推荐使用Runnable对象,因为Java单继承的局限性
package com.waves.dxcdemo; public class TestThread3 implements Runnable{ @Override public void run() { //线程方法体 for (int i = 0; i < 200; i++) { System.out.println("2021-11-28 18:22 多线程代码"+i); } } public static void main(String[] args) { //main方式,主线程 TestThread3 thread3 = new TestThread3(); //创建线程对象,通过线程对象来开启我们的线程,也就是代理 // Thread thread = new Thread(thread3); // thread.start(); //合并为一句代码 new Thread(thread3).start(); for (int i = 0; i < 200; i++) { System.out.println("2021-11-28 18:22 学习多线程代码"+ i); } } }
继承Thread类和实现Runnable接口-小结:
-
继承Thread类
-
子类继承Thread类具备多线程能力
-
启动线程:子类对象.start()
-
不建议使用:避免OOP单继承局限性
-
-
实现Runnable接口
-
实现接口Runnable具有多线程能力
-
启动线程:传入目标对象+Thread对象.start()
-
1.实现runnable接口 买票案例 (线程不安全)
package com.waves.javathread.threadweb; /** * 多线程同时操作一个对象 * 买火车票例子 * * 发现问题:多个线程操作同一个资源的情况下,线程不安全 ,会造成数据紊乱 * */ public class ThreadImplRunnable01 implements Runnable{ private int titckNumbers = 20; @Override public void run() { while (true){ if (titckNumbers<=0){ break; } //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-->买到了第"+titckNumbers--+"票"); } } public static void main(String[] args) { ThreadImplRunnable01 threadImplRunnable01 = new ThreadImplRunnable01(); new Thread(threadImplRunnable01,"买票者1").start(); new Thread(threadImplRunnable01,"买票者2").start(); new Thread(threadImplRunnable01,"买票者3").start(); } }
2.实现runnable接口 模拟龟兔赛跑
package com.waves.javathread.threadweb; /** * 模拟龟兔赛跑 */ public class ThreadImplRunnable02 implements Runnable{ private static String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { if (Thread.currentThread().getName().equals("兔子") && i == 50){ try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束了 if (success(i)){ break; } System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"不"); } } private boolean success(int pages){ //判断是否有获胜者 if (winner != null){ return true; }{ if (pages>=100){ winner = Thread.currentThread().getName(); System.out.println("获胜者是--->"+winner); return true; } } return false; } public static void main(String[] args) { ThreadImplRunnable02 threadImplRunnable02 = new ThreadImplRunnable02(); new Thread(threadImplRunnable02,"乌龟").start(); new Thread(threadImplRunnable02,"兔子").start(); } }
-
-
重写call方法,需要抛出异常
-
创建目标对象
-
创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
-
提交执行: Future<Boolean> result1 = ser.submit(xiancheng1);
-
获取结果: boolean r1 = result1.get();
-
关闭服务: ser.shutdown();
package com.waves.javathread.threadweb.CallableInterface; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*; /** * 2023-0204 17:07:23 * 狂神多线程 * 线程创建方式三:实现callable接口 * callable的好处 * 1.可以定义返回值 * 2.可以抛出异常 */ public class ImplCallableDemo implements Callable<Boolean> { private String url; //下载图片的地址 private String name; //要下载(保存)的图片名字哦 public ImplCallableDemo(String url,String name){ this.url = url; this.name =name; } @Override public Boolean call() { WedDownLoader wedDownLoader = new WedDownLoader(); wedDownLoader.downLoader(url,name); //图片保存在根目录 System.out.println("下载图片路径为"+url+"\n"+"下载的图片名字为"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { ImplCallableDemo xiancheng1 = new ImplCallableDemo("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fd.zdqx.com%2Fshirt_20190710%2F014.jpg&refer=http%3A%2F%2Fd.zdqx.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641202820&t=637cea2d77c04500bdbef46abce0e64e","动漫桌面1.jpg"); ImplCallableDemo xiancheng2 = new ImplCallableDemo("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fba794af4e3c6a4cf93b25f19e148dc4288e58b91.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203074&t=0b47d29f43a86fc5a2adae8bb5b60748","动漫桌面2.jpg"); ImplCallableDemo xiancheng3 = new ImplCallableDemo("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fc099d5c83bdffb17eff062d5298eb136f558b8fd.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203131&t=490c5ea40c0745215a8140d8118f22ec","动漫桌面3.jpg"); // 1. 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(3); // 2. 提交执行: Future<Boolean> result1 = ser.submit(xiancheng1); Future<Boolean> result2 = ser.submit(xiancheng2); Future<Boolean> result3 = ser.submit(xiancheng3); // 3. 获取结果: boolean r1 = result1.get(); boolean r2 = result1.get(); boolean r3 = result1.get(); // 4. 关闭服务: ser.shutdown(); } } //下载器 class WedDownLoader{ 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下载异常"); } } }
Lamdba表达式
静态代理
package com.waves.javathread.staticproxy; /** * @author waves * @date 2023/2/4 17:19 * * 狂神多线程 静态代理: * 真实对象和代理对象都要实现同一个接口 * 代理对象要代理真实角色 * * 好处: * 代理对象可以做很多真实对象做不了的事情 * 真实对象专注做自己的事情 */ public class StaticProxy { public static void main(String[] args) { //lamda表达式写法 new Thread(()-> System.out.println("I LOVE YOU!!!")).start(); WeddingCompany weddingCompany = new WeddingCompany(new You()); weddingCompany.HappMarry(); } } interface Marry{ void HappMarry(); } //实际角色 你去结婚 class You implements Marry{ @Override public void HappMarry() { System.out.println("你要结婚了,超开心!"); } } //代理角色,帮助你 class WeddingCompany implements Marry{ //代理谁 --->>> 真实目标角色 private Marry target; public WeddingCompany(Marry target){ this.target = target; } @Override public void HappMarry() { before(); this.target.HappMarry(); //真实对象 after(); } private void after() { System.out.println("结婚之后,收尾款"); } private void before() { System.out.println("结婚之前,布置现场"); } }
package com.waves.javathread.lamdba; /** * @author waves * @date 2023/2/5 16:51 * 推导lamdba表达式 */ public class LamdbaDemo1 { //3.静态内部类 static class Like2 implements ILike{ @Override public void LamdbaIt() { System.out.println("i Like2 lamdba"); } } public static void main(String[] args) { ILike iLike = null; iLike = new Like(); iLike.LamdbaIt(); iLike = new Like2(); iLike.LamdbaIt(); //4.局部内部类 class Like3 implements ILike{ @Override public void LamdbaIt() { System.out.println("i Like3 lamdba"); } } iLike = new Like3(); iLike.LamdbaIt(); //5.匿名内部类, 没有类的名称,必须借助接口或者父类 iLike = new ILike() { @Override public void LamdbaIt() { System.out.println("i Like4 lamdba"); } }; iLike.LamdbaIt(); //6.借助Lamdba表达式简化 iLike = ()-> { System.out.println("i Like5 lamdba"); }; iLike.LamdbaIt(); } } //1.定义一个函数式接口 interface ILike{ void LamdbaIt(); } //2.实现类 class Like implements ILike{ @Override public void LamdbaIt() { System.out.println("i like lamdba"); } }
package com.waves.javathread.lamdba; /** * @author waves * @date 2023/2/5 17:03 * * 下列使用lamdba方式效果是一样的,但是简写了 * * 总结: * lamdba表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹. * 前提是接口为函数式接口 * 多个参数也可以去掉参数类型,要去掉就都去掉,多参数必须加括号 */ public class LamdbaDemo2 { public static void main(String[] args) { ILove love = null; ILoveD loved = null; /*** * 单参数 */ //1.lamdba表达式简化 love = (String str)->{ System.out.println("i love you ---> "+str); }; //2.简化 参数类型 love = (str) ->{ System.out.println("i love you ---> "+str); }; //3. 简化括号 love = str ->{ System.out.println("i love you ---> "+str); }; //4. 简化去掉花括号 love = str -> System.out.println("i love you ---> "+str); love.love("小明"); /** * 多参数 多参数 */ //1.lamdba表达式简化 loved = (String str,String str2)->{ System.out.println(str + " love you ---> "+str2); }; //2.简化 参数类型 loved = (str,str2) ->{ System.out.println(str + " love you ---> "+str2); }; loved.loved("小明","小红"); } } interface ILove{ void love(String str); } interface ILoveD{ void loved(String str,String str2); }
线程休眠
-
sleep(时间)指定当前线程阻塞的毫秒数
-
sleep存在InterruptedException
-
sleep时间达到后线程进入就绪状态
-
-
线程礼让
-
礼让线程,让当前正在执行的线程暂停,但不阻塞
-
将线程从运行状态转为有序状态
-
让CPU重新调度,礼让不一定成功!看CPU心情
package com.waves.javathread.state; /** * @author waves * @date 2023/2/7 21:52 * * 测试线程礼让 *但是礼让并不一定成功,看cpu心情 */ public class ThreadYieldDemo { } class MyYidld implements Runnable{ public static void main(String[] args) { MyYidld myYidld = new MyYidld(); new Thread(myYidld,"小明").start(); new Thread(myYidld,"小红").start(); } @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield(); //礼让 System.out.println(Thread.currentThread().getName()+"线程结束执行"); } }
-
join合并线程,待此线程执行完成过后,在执行其他线程,其他线程阻塞
-
可以想象成插队
package com.waves.javathread.state; /** * @author waves * @date 2023/2/7 22:03 */ public class ThreadJoinDemo implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程VIP来了"+i); } } public static void main(String[] args) throws InterruptedException { //启动线程 ThreadJoinDemo threadJoinDemo = new ThreadJoinDemo(); Thread thread = new Thread(threadJoinDemo); thread.start(); //主线程 for (int i = 0; i < 100; i++) { if (i == 88){ thread.join(); } System.out.println("main"+i); } } }
线程池
-
背景:经常创建和销毁,使用量 特别大的资源,例如:并发情况下的线程,对性能影响很大.
-
思路:提前创建多个线程,放入线程池,使用时直接获取,使用完放回池中.可以避免频繁创建销毁,实现重复利用. 例如生活中的公共交通工具
-
好处 :
-
提高响应速度(减少了创建新线程 的时间)
-
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
-
便于线程管理
-
corePoolSize:核心池子大小
-
maxmumPoolSize:最大线程数
-
keepAliveTime:线程 没有任务时最多保持多长时间后会终止
-
-
-