*多线程实现的通常用法:
1.继承线程或者实现Runnable接口
2.重写run()方法
3.创建一个主函数main函数,从中新建一个这个类的线程的实例化对象,通过创建多个实例化对象从而实现多线程
一、进程与线程
*首先,说起进程,就要说起程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。 *而进程则是执行程序的一次执行过程,他是一个动态的概念。是系统资源分配的单位 *通常在一个进程可以包含多个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是cpu调度和执行的单位。 注:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAkR7dVY-1657988963695)(C:\Users\SirFc\AppData\Local\Temp\1656689479885.png)]
二、普通方法和多线程方法和线程创建的第一种方法
首先普通方法为run()方法。当新创建一个线程的实例化对象时,使用run()方法,则会先调用run()方法中的代码 多线程方法为start()方法,他是多个线程同时进行的方法 代码如下: //创建线程的方式一:继承Thread类,重写一个run()方法,调用start类开启线程 public class TestThread1 extends Thread { @Override public void run(){ // run方法线程体 for (int i=0;i<10;i++){ System.out.println("我喜欢学习Java"+i); } } public static void main(String[] args) { // 调用run()方法的线程 // 1.创建一个线程对象 TestThread1 ts=new TestThread1(); // 2.调用start()方法 ts.run(); ts.start(); // main线程,主线程 for (int i = 0; i < 20; i++) { System.out.println("我在学习多线程!"+i); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-au4wFd65-1657988963697)(C:\Users\SirFc\AppData\Local\Temp\1656691986888.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oiYZO2iV-1657988963697)(C:\Users\SirFc\AppData\Local\Temp\1656692176471.png)]
三、多线程图片下载
1.首先要引用一个文件流的包,maven项目则引用依赖,依赖如下: <dependencies> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> </dependencies> 2.其次。创建一个文件下载器 //下载器 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方法出现问题"); } } 3.写主方法类中的代码 //练习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 wd=new WebDownloader(); wd.downloader(url,name); System.out.println("下载好的文件名为:"+name); } public static void main(String[] args) { TestThread2 ts = new TestThread2("图片的url", "图片1"); TestThread2 ts1 = new TestThread2("图片的url","图片2"); TestThread2 ts2 = new TestThread2("图片的url","图片3"); ts.start(); ts1.start(); ts2.start(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1rEjV2M-1657988963698)(C:\Users\SirFc\AppData\Local\Temp\1656756204993.png)]
四、创建线程的第二种方法,实现Runnable接口
//创建线程的第2种方法:实现Runnable接口,重写run方法,执行接口需要调用Runnable接口的实现类,调用start()方法。 public class TestThread3 implements Runnable{ @Override public void run() { // run方法线程体 for (int i = 0; i < 200; i++) { System.out.println("我爱学java!"+i); } } public static void main(String[] args){ // 创建一个runnable接口的实现类对象 TestThread3 ts=new TestThread3(); // 创建一个线程,通过线程对象来开启我们的线程,代理(将原有创建线程丢到这个新的空线程中进行代理!) // Thread thread = new Thread(ts); // thread.start(); // 这一行代码可以顶如上两行代码 new Thread(ts).start(); for (int i = 0; i < 200; i++) { System.out.println("我爱学习!"+i); } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7RXgC5wK-1657988963699)(C:\Users\SirFc\AppData\Local\Temp\1656756303122.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NKt87W1p-1657988963699)(C:\Users\SirFc\AppData\Local\Temp\1656773457902.png)]
五、多个线程操作同一个对象
(未加琐状态,有数据紊乱现象)
//多个线程操作同一个数据,买火车票的例子 //发现问题:多个线程同时操作一个数据的时候,线程不安全,数据紊乱 public class TestThread4 implements Runnable { // 票数 private int ticket=10; @Override public void run() { while (true){ if (ticket<=0){ break; } ticket--; // 模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { System.out.println("您的购票请求已经超时"); } // Thread.currentThread().getName()方法可以获取线程创建的Name System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket+"张票"); } } public static void main(String[] args) { TestThread4 ts=new TestThread4(); new Thread(ts,"小明").start(); new Thread(ts,"黄牛党").start(); new Thread(ts,"小付").start(); } } 有同一张票多个人拿到
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-THF3RSxO-1657988963700)(C:\Users\SirFc\AppData\Local\Temp\1656775757953.png)]
解决问题案例(龟兔赛跑)
//模拟龟兔赛跑 public class Race implements Runnable{ // 胜利者 private String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { // 模拟兔子休息 if (Thread.currentThread().getName().equals("兔子")&&i%10==0){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } }; // 如果比赛结束了 if (gameOver(i)){ break; } System.out.println(Thread.currentThread().getName()+"跑了"+i+"步!"); } } // 判断是否完成 private boolean gameOver(int step){ if (winner!=null){ return true; } { if (step>=100){ winner=Thread.currentThread().getName(); System.out.println(winner+"是胜利者"); return true; } } return false; } public static void main(String[] args) { Race race=new Race(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y3DfVm4q-1657988963700)(C:\Users\SirFc\AppData\Local\Temp\1656778128822.png)]
六、实现Callable接口(了解即可)
//线程创建三:实现Callable接口 public class TestCallable implements Callable<Boolean> { private String url; //网络图片地址 private String name; //保存的文件名 public TestCallable(String url,String name){ this.url=url; this.name=name; } // 下载线程的执行体 @Override public Boolean call() throws Exception { WebDownloader wd=new WebDownloader(); wd.downloader(url,name); System.out.println("下载文件名未"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable ts=new TestCallable("https://ts4.cn.mm.bing.net/th?id=OIP-C.kB-Ovasi0GW67-rmwnAcwAHaEo&w=316&h=197&c=8&rs=1&qlt=90&o=6&dpr=1.38&pid=3.1&rm=2","付"); TestCallable ts1=new TestCallable("https://ts4.cn.mm.bing.net/th?id=OIP-C.kB-Ovasi0GW67-rmwnAcwAHaEo&w=316&h=197&c=8&rs=1&qlt=90&o=6&dpr=1.38&pid=3.1&rm=2","付"); TestCallable ts2=new TestCallable("https://ts4.cn.mm.bing.net/th?id=OIP-C.kB-Ovasi0GW67-rmwnAcwAHaEo&w=316&h=197&c=8&rs=1&qlt=90&o=6&dpr=1.38&pid=3.1&rm=2","付"); // 创建执行服务 ExecutorService service= Executors.newFixedThreadPool(3); // 提交执行 Future<Boolean> r1=service.submit(ts); Future<Boolean> r2=service.submit(ts1); Future<Boolean> r3=service.submit(ts2); // 获取结果 boolean rs1=r1.get(); boolean rs2=r2.get(); boolean rs3=r3.get(); // 打印返回职 System.out.println(rs1); System.out.println(rs2); System.out.println(rs3); // 关闭服务 : service.shutdownNow(); } } //下载器 class WebDownloader{ public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); System.out.println("下载成功!"); } catch (IOException e) { System.out.println("下载失败!"); } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mihshqoC-1657988963701)(C:\Users\SirFc\AppData\Local\Temp\1656862281736.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9e077RUu-1657988963702)(C:\Users\SirFc\AppData\Local\Temp\1657035759200.png)]
七、静态代理
静态代理模式总结: 1、真实对象和代理对象都要实现同一个接口 2、代理对象要代理真实角色 好处: 1)、代理对象可以做很多真实对象做不了的事情 2)、真实对象可以专注做自己的事情 //静态代理,可表示一个婚庆公司,本人去结婚,婚庆公司去布置现场 public class StacticProxy { public static void main(String[] args) { WeddingCompany wc=new WeddingCompany(new You()); wc.HappyMarry(); } } interface Marry{ // 人生四大喜事 // 久旱逢甘霖 // 他乡遇故知 // 洞房花烛夜 // 金榜题名时 void HappyMarry(); } //这个人是你,你去结婚 class You implements Marry{ @Override public void HappyMarry() { System.out.println("付今天去结婚,很开心!"); } } // 婚庆公司,代表你去置办婚礼 class WeddingCompany implements Marry{ // 代理谁?-》真实目标角色 private Marry target; public WeddingCompany (Marry target){ this.target=target; } @Override public void HappyMarry() { before(); // 这里要传的值是最终得到的值! this.target.HappyMarry();//这就是真实对象 after(); } private void after() { System.out.println("结婚之后,去收拾场地"); } private void before() { System.out.println("结婚之前,去布置场地"); } }
八、Lamda表达式(注意:必须要满足函数式接口,才能用Lamda表达式)
Lamda表示式是用于Java8之后的, 函数式接口定义: 1.*任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口,如: public interface Runnable{ public abstract void run(); } *反例:像这样一个接口中有多个方法的就不是,只有一个接口中有一个方法的才是 public interface Runnable{ public abstract void run(); public abstract void start(); } 2.*对于函数式接口,我们可以通过lamda表达式来创建接口的对象 注意:必须要满足函数式接口,才能用Lamda表达式 /* * 推导Lamda表达式*/ public class TestLamda { // 3.静态内部类:把实现类放到静态内部类当中 static class like2 implements ILike{ @Override public void run() { System.out.println("我喜欢lamda2"); } } // mian方法 public static void main(String[] args) { // 输出的是下面的实现类的 ILike like = new like(); like.run(); // 输出的静态类中的方法 like = new like2(); like.run(); // 4.局部内部类 class like3 implements ILike{ @Override public void run() { System.out.println("我是lamda3"); } } like =new like3(); like.run(); // 5.匿名内部类,没有类的名称,必须借助接口或者父类,注意,类的后方括号必须加分号(;),因为它相当于一个方法的调用。 like = new ILike() { @Override public void run() { System.out.println("我是lamda4"); } }; like.run(); // 6.用lamda简化(目的就是将接口的调用和实现完成,所以要求这个接口中只有一个方法,才能调用lamda表达式) like = () ->{ System.out.println("我是lamda5"); }; like.run(); } } //1.创建一个接口 interface ILike{ public void run(); } //2.创建一个实体类来实现接口 class like implements ILike{ @Override public void run() { System.out.println("我喜欢lamda"); } }
九、线程状态(五大状态)
1.创建状态 Thread t=new Thread();线程一旦创建就到了新生状态 2.就绪状态 当线程调用了start()方法,线程立即进入到就绪状态,但不意味着立即调度执行 3.阻塞状态 当调用sleep,wait或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞状态解除后,重新进入就绪状态,等待cpu的调度执行。 4.运行状态 当程序进入到运行状态的时候,才真正开始执行线程体的代码块 5.死亡状态 线程中断或者结束,一旦进入死亡状态,就不能再次启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3R5rAJfw-1657988963702)(C:\Users\SirFc\AppData\Local\Temp\1657547778978.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e5KJkO5a-1657988963703)(C:\Users\SirFc\AppData\Local\Temp\1657547928918.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yISmIitU-1657988963704)(C:\Users\SirFc\AppData\Local\Temp\1657548478802.png)]
1.线程停止
/测试Stop //1.建议线程正常停止--->利用次数,不建议死循环 //2.建议使用标志位--->设置一个标志位 //3.不要使用stop或者destroy等过时或者JDK不建议使用的方法 public class TestStop implements Runnable { // 设置一个标志位 private boolean flag=true; @Override public void run() { int i=0; while (flag){ System.out.println("run...Thread"+i++); } } public void stop(){ this.flag=false; } public static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 99; i++) { System.out.println("mian"+i); if (i==98){ testStop.stop(); System.out.println("线程停止了!"); } } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ahgF3h2G-1657988963704)(C:\Users\SirFc\AppData\Local\Temp\1657549315082.png)]
2.线程休眠(Sleep:每个对象都有一把琐,sleep不会释放琐,Thread.sleep()方法能让问题显露的更清晰)
//模拟网络延时: 放大问题的发生性 public class TestSleep implements Runnable { // 票数 private int ticket=100; @Override public void run() { while (true){ if (ticket<=0){ break; } ticket--; // 模拟延迟 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); } } public static void main(String[] args) { TestSleep testSleep = new TestSleep(); new Thread(testSleep,"小明").start(); new Thread(testSleep,"小红").start(); new Thread(testSleep,"小付").start(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4EJi1lm-1657988963705)(C:\Users\SirFc\AppData\Local\Temp\1657551180215.png)]
3.线程礼让
//测试礼让线程,看cpu心情 public class TestYield { public static void main(String str[]){ Yield yield = new Yield(); new Thread(yield,"a").start(); new Thread(yield,"b").start(); } } //礼让代码 class Yield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"线程结束"); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpCOc93U-1657988963705)(C:\Users\SirFc\AppData\Local\Temp\1657553684385.png)]
4.join(相当于一个vip必须它的所有线程全部跑完,别的线程才能继续跑)
//测试join方法 public class TestJoin implements Runnable { // vip线程 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("VIP来了:"+i); } } public static void main(String[] args) throws InterruptedException { // 主线程 TestJoin join=new TestJoin(); Thread thread=new Thread(join); thread.start(); for (int i = 0; i < 500; i++) { System.out.println("我是:"+i); if (i==50){ thread.join(); } } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K95El5ok-1657988963706)(C:\Users\SirFc\AppData\Local\Temp\1657554435481.png)]
十、线程状态观测
//测试线程观测状态 public class TestState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for (int i = 0; i < 5; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("i="+i); } }); // 观察状态 Thread.State state=thread.getState(); System.out.println(state);//new // 观察启动状态 thread.start();//启动线程 state = thread.getState(); System.out.println(state); while (state !=Thread.State.TERMINATED){ // 只要线程不终止 Thread.sleep(100); // 更新线程状态 state = thread.getState(); System.out.println(state); } // 线程死亡后不能在启动! thread.start(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZODVpbP-1657988963707)(C:\Users\SirFc\AppData\Local\Temp\1657635796853.png)]
十一、线程优先级(线程的优先级就像中彩票一样,越多,中奖几率越大,而不是1是最大优先级,数字越大,代表优先级越大,范围是1-10,但并不是高的先执行,只是概率大,大多数时候是高的先执行,必须先设置优先级再启动项目)
//测试线程的优先级 public class TestPriority extends Thread { public static void main(String[] args) { MyPriority myPriority = new MyPriority(); Thread thread = new Thread(myPriority); Thread thread1 = new Thread(myPriority); Thread thread2 = new Thread(myPriority); Thread thread3 = new Thread(myPriority); Thread thread4 = new Thread(myPriority); Thread thread5 = new Thread(myPriority); // 顾名思义,线程的优先级范围为1-10,系统默认优先级为5 thread.setPriority(1); thread.start(); thread1.setPriority(MAX_PRIORITY); thread1.start(); thread2.setPriority(5); thread2.start(); thread3.setPriority(7); thread3.start(); thread4.setPriority(-1); thread4.start(); thread5.setPriority(0); thread5.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-96480HIp-1657988963707)(C:\Users\SirFc\AppData\Local\Temp\1657636683048.png)]
十二、线程同步机制(多个线程操作同一个资源)
并发:同一个对象被多个线程同时操作 线程同步:就是解决像现实生活当中遇到的问题:“同一个资源,多个人想使用”的问题,最天然的办法就是:排队,一个一个来。 处理多线程的问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步,线程同步其实就是等待机制,多个需要同时访问此对象的线程进入这个对象等待池形成队列,等待前面线程使用完毕,下一个线程再使用
队列和锁
public class TEstLock { public static void main(String[] args) { Tickes tickes = new Tickes(); new Thread(tickes).start(); new Thread(tickes).start(); new Thread(tickes).start(); } } class Tickes implements Runnable { private int tickes = 10; // 定义Lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); // 加锁 if (tickes <= 0) { return; } Thread.sleep(1000); System.out.println(tickes--); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); // 解锁 } } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H5YjECGt-1657988963708)(C:\Users\SirFc\AppData\Local\Temp\1657724345967.png)]
十三、线程三大不安全案例
1.线程不安全的集合(以ArrayList为例)
注意:为什么ArrayList线程不安全,因为当使用array.add方法时,可能多个线程同时进行这个方法,从而先创建的线程被后创建的线程覆盖 //线程不安全的集合 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> arrayList = new ArrayList<String>(); for (int i = 0; i < 100000; i++) { arrayList.add(Thread.currentThread().getName()); } try { // 添加Sleep方法,让线程睡眠,更有说服力,正常想要的结果是不为10000,但是大部分时间为10000的原因是cpu执行太快,没得到预期结果 // 为啥ArrayList是线程不安全的,因为它可能俩个或者多个线程共同执行Array.add()操作,导致先创建的被后创建的所覆盖 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(arrayList.size()); } }