程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
进程则是执行程序的一次执行过程,他是一个定态的概念。是系统资源分配的单位
一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,记载一个CPU的情况下,在同发一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
核心概念
1.线程就是独立的执行的路径;
2.在程序执行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
3.main()称之为主线程,为系统的入口,用于执行整个程序;
4.在一个进程中,如果开辟了多个线程,线程的运行由调度器(cpu)安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的;
5.对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
6.线程会带来额外开销(需要去调它),如cpu调度时间,并发控制开销(排队);
7.每个线程在自己的工作内存交互,内存控制不当会在成数据不一致。
1.Thread class ===> 继承Thread类
2.Runnable接口 ===> 实现Runnable接口
3.Callable接口 ===> 实现Callable接口
1.自定义线程类继承Thread类
2.重写run()方法,编写线程执行体
3.创建线程对象,调用start()方法启动线程
线程不一定立即执行,CPU安排调度
调用start()方法:
package thread; //创建线程方式一:继承Thread类,重写run()方法,调用start()开启线程 //总结:注意,线程开启不一定立即执行,由CPU调度执行 public class TestThread01 extends Thread{ //线程入口点 @Override public void run() { //run()方法线程体 for (int i = 0; i < 20; i++) { System.out.println("我在看代码------"+i); } } public static void main(String[] args) { //main()线程,主线程 //创建一个线程对象 TestThread01 testThread01 = new TestThread01(); //调用start()方法开启线程 testThread01.start(); for (int i = 0; i < 20; i++) { System.out.println("我在学习多线程------"+i); } } }
运行结果:
我在学习多线程------0 我在学习多线程------1 我在学习多线程------2 我在学习多线程------3 我在学习多线程------4 我在学习多线程------5 我在学习多线程------6 我在看代码------0 我在看代码------1 我在看代码------2 我在看代码------3 我在看代码------4 我在学习多线程------7 我在看代码------5 我在看代码------6 我在看代码------7 我在看代码------8 我在看代码------9 我在看代码------10 我在学习多线程------8 我在学习多线程------9 我在学习多线程------10 我在学习多线程------11 我在学习多线程------12 我在学习多线程------13 我在看代码------11 我在学习多线程------14 我在学习多线程------15 我在学习多线程------16 我在学习多线程------17 我在学习多线程------18 我在学习多线程------19 我在看代码------12 我在看代码------13 我在看代码------14 我在看代码------15 我在看代码------16 我在看代码------17 我在看代码------18 我在看代码------19 Process finished with exit code 0
调用run()方法:
public static void main(String[] args) { //main()线程,主线程 //创建一个线程对象 TestThread01 testThread01 = new TestThread01(); //调用start()方法开启线程 testThread01.run(); for (int i = 0; i < 20; i++) { System.out.println("我在学习多线程------"+i); } }
运行结果:
我在看代码------0 我在看代码------1 我在看代码------2 我在看代码------3 我在看代码------4 我在看代码------5 我在看代码------6 我在看代码------7 我在看代码------8 我在看代码------9 我在看代码------10 我在看代码------11 我在看代码------12 我在看代码------13 我在看代码------14 我在看代码------15 我在看代码------16 我在看代码------17 我在看代码------18 我在看代码------19 我在学习多线程------0 我在学习多线程------1 我在学习多线程------2 我在学习多线程------3 我在学习多线程------4 我在学习多线程------5 我在学习多线程------6 我在学习多线程------7 我在学习多线程------8 我在学习多线程------9 我在学习多线程------10 我在学习多线程------11 我在学习多线程------12 我在学习多线程------13 我在学习多线程------14 我在学习多线程------15 我在学习多线程------16 我在学习多线程------17 我在学习多线程------18 我在学习多线程------19 Process finished with exit code 0
普通方法调用和多线程
总结:注意,线程开启不一定立即执行,由CPU调度执行
(调用start()方法是开启一个线程,然后线程会调用run()方法,如果直接调用run()方法那和定义一个对象,然后调用了对象中的一个方法没有区别,根本没有创建新的进程)
package thread; //注意:一个java文件中,可以定义多个类,但只能有一个public类 import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //练习Thread,实现多线程同步下载图片 public class TestThread02 extends Thread{ private String url;//网络图片地址 private String name;//保存的文件名 public TestThread02(String url,String name){ this.url = url; this.name = name; } //下载图片线程的执行体 @Override public void run() { WebDownload webDownload = new WebDownload(); webDownload.downloader(url,name); System.out.println("下载了文件名为:"+name); } public static void main(String[] args) { String url1 = "https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=%E8%B7%AF%E9%A3%9E&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=3765507157,583221466&os=763603878,172912997&simid=3765507157,583221466&pn=37&rn=1&di=135190&ln=1650&fr=&fmq=1640444510388_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fi0.hdslb.com%252Fbfs%252Farticle%252Fb224524798157bf6a1a930c36900d07cec8e2ef6.jpg%26refer%3Dhttp%253A%252F%252Fi0.hdslb.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036513%26t%3D4f472c2960c9327a57f6b01bba6b3a90&rpstart=0&rpnum=0&adpicid=0&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D&ctd=1640444689019^3_1579X621%1"; String url2 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E8%89%BE%E6%96%AF&step_word=&hs=0&pn=10&spn=0&di=86460&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=219613496%2C2423385425&os=244226930%2C1703390822&simid=219613496%2C2423385425&adpicid=0&lpn=0&ln=1593&fr=&fmq=1640444879402_R&fm=result&ic=&s=undefined&hd=&latest=©right=&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F23%2F20210723140554_8a24f.thumb.1000_0.png%26refer%3Dhttp%3A%2F%2Fc-ssl.duitang.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036879%26t%3D573104878cca8c90c93593482068ae3b&gsm=1e&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D"; String url3 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E8%90%A8%E5%8D%9A&step_word=&hs=0&pn=34&spn=0&di=20020&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=1538742093%2C620552439&os=1291420748%2C3189193177&simid=3181336095%2C3605445912&adpicid=0&lpn=0&ln=1637&fr=&fmq=1640444809527_R&fm=result&ic=&s=undefined&hd=&latest=©right=&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201612%2F25%2F20161225161314_fUCsM.thumb.400_0.jpeg%26refer%3Dhttp%3A%2F%2Fc-ssl.duitang.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036858%26t%3D0e17573db0c86d58a5545185cb5edf17&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B17tpwg2_z%26e3Bv54AzdH3Fwsk74AzdH3F%3Ft1%3Dldllanda%26fr4%3Dda89_z%26e3B8dccnmbb_z%26e3Bdad_z%26e3Ba&gsm=23&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D"; TestThread02 t1 = new TestThread02(url1,"路飞.jpg"); TestThread02 t2 = new TestThread02(url2,"艾斯.jpg"); TestThread02 t3 = new TestThread02(url3,"萨博.jpg"); t1.start(); t2.start(); t3.start(); } } //下载器 class WebDownload{ //下载方法 public void downloader(String url,String name){ try { //copyURLToFile():网页地址变成文件 下载的地址:默认当前项目路径下 FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloade方法出现问题"); } } }
运行结果:(调用start()方法每次下载顺序不同)
1.定义MyRunnable类实现Runnable接口
2.实现run()方法,编写线程体
3.创建线程对象,调用start()方法启动线程
推荐使用Runnable对象,因为Java单继承的局限性
package thread; //创建线程方式二:实现Runnabke接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法 public class TestThread03 implements Runnable{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("我在看代码------"+i); } } public static void main(String[] args) { //创建Runnable接口的实现类对象 TestThread03 testThread03 = new TestThread03(); //创建线程对象,通过线程对象来开启我们的线程,代理 //Thread thread = new Thread(testThread03); //thread.start(); //简写方式 new Thread(testThread03).start(); for (int i = 0; i < 20; i++) { System.out.println("我在学习多线程------"+i); } } }
运行结果:
我在学习多线程------0 我在看代码------0 我在看代码------1 我在看代码------2 我在看代码------3 我在看代码------4 我在学习多线程------1 我在看代码------5 我在学习多线程------2 我在看代码------6 我在看代码------7 我在看代码------8 我在看代码------9 我在学习多线程------3 我在学习多线程------4 我在看代码------10 我在学习多线程------5 我在看代码------11 我在学习多线程------6 我在看代码------12 我在学习多线程------7 我在看代码------13 我在学习多线程------8 我在看代码------14 我在学习多线程------9 我在看代码------15 我在学习多线程------10 我在看代码------16 我在学习多线程------11 我在看代码------17 我在学习多线程------12 我在看代码------18 我在学习多线程------13 我在看代码------19 我在学习多线程------14 我在学习多线程------15 我在学习多线程------16 我在学习多线程------17 我在学习多线程------18 我在学习多线程------19 Process finished with exit code 0
package thread; //注意:一个java文件中,可以定义多个类,但只能有一个public类 import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //练习Thread,实现多线程同步下载图片 public class TestThread04 implements Runnable{ private String url;//网络图片地址 private String name;//保存的文件名 public TestThread04(String url,String name){ this.url = url; this.name = name; } //下载图片线程的执行体 @Override public void run() { WebDownload webDownload = new WebDownload(); webDownload.downloader(url,name); System.out.println("下载了文件名为:"+name); } public static void main(String[] args) { String url1 = "https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=%E8%B7%AF%E9%A3%9E&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=3765507157,583221466&os=763603878,172912997&simid=3765507157,583221466&pn=37&rn=1&di=135190&ln=1650&fr=&fmq=1640444510388_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fi0.hdslb.com%252Fbfs%252Farticle%252Fb224524798157bf6a1a930c36900d07cec8e2ef6.jpg%26refer%3Dhttp%253A%252F%252Fi0.hdslb.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036513%26t%3D4f472c2960c9327a57f6b01bba6b3a90&rpstart=0&rpnum=0&adpicid=0&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D&ctd=1640444689019^3_1579X621%1"; String url2 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E8%89%BE%E6%96%AF&step_word=&hs=0&pn=10&spn=0&di=86460&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=219613496%2C2423385425&os=244226930%2C1703390822&simid=219613496%2C2423385425&adpicid=0&lpn=0&ln=1593&fr=&fmq=1640444879402_R&fm=result&ic=&s=undefined&hd=&latest=©right=&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F23%2F20210723140554_8a24f.thumb.1000_0.png%26refer%3Dhttp%3A%2F%2Fc-ssl.duitang.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036879%26t%3D573104878cca8c90c93593482068ae3b&gsm=1e&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D"; String url3 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E8%90%A8%E5%8D%9A&step_word=&hs=0&pn=34&spn=0&di=20020&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=1538742093%2C620552439&os=1291420748%2C3189193177&simid=3181336095%2C3605445912&adpicid=0&lpn=0&ln=1637&fr=&fmq=1640444809527_R&fm=result&ic=&s=undefined&hd=&latest=©right=&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201612%2F25%2F20161225161314_fUCsM.thumb.400_0.jpeg%26refer%3Dhttp%3A%2F%2Fc-ssl.duitang.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036858%26t%3D0e17573db0c86d58a5545185cb5edf17&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B17tpwg2_z%26e3Bv54AzdH3Fwsk74AzdH3F%3Ft1%3Dldllanda%26fr4%3Dda89_z%26e3B8dccnmbb_z%26e3Bdad_z%26e3Ba&gsm=23&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D"; TestThread04 t1 = new TestThread04(url1,"路飞.jpg"); TestThread04 t2 = new TestThread04(url2,"艾斯.jpg"); TestThread04 t3 = new TestThread04(url3,"萨博.jpg"); new Thread(t1).start(); new Thread(t2).start(); new Thread(t3).start(); } } //下载器 class WebDownload{ //下载方法 public void downloader(String url,String name){ try { //copyURLToFile():网页地址变成文件 下载的地址:默认当前项目路径下 FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloade方法出现问题"); } } }
运行结果:
package thread; //多个线程同时操作同一个对象 //买火车票的例子 public class TestThread05 implements Runnable{ //票数 private int ticketNums = 10; @Override public void run() { while (ticketNums > 0){ //模拟延时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { TestThread05 ticket = new TestThread05(); new Thread(ticket,"小程").start(); new Thread(ticket,"小白").start(); new Thread(ticket,"小黑").start(); } }
运行结果:
发现问题:多个线程操作同一个自愿的情况下,线程不安全,数据紊乱。(后面讲同步时会解决)
package thread; //模拟柜体赛跑 public class Race implements Runnable{ //static 共享 final 不变 private static String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { //模拟兔子休息 if(Thread.currentThread().getName().equals("兔子") && i%20 == 0){ try { Thread.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 boolean flag = gameOver(i); if(flag){ break; } System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步"); } } //判断是否完成比赛 public boolean gameOver(int steps){ //判断是否有胜利者 if(winner != null){//已经存在胜利者了 return true; } if(steps >= 100){ winner = Thread.currentThread().getName(); System.out.println("Winner is "+ winner); return true; } return false; } public static void main(String[] args) { Race race = new Race(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }
运行结果:
龟兔赛跑自己的版本:
package thread; //龟兔赛跑 //乌龟每次跑1步 兔子每次跑两步 public class TortoiseAndHareRace { //胜利者 private static String winner; public static void main(String[] args) { Tortoise tortoise = new Tortoise(); Hare hare = new Hare(); new Thread(tortoise,"乌龟").start(); new Thread(hare,"兔子").start(); } public static boolean gameOver(int steps){ if(winner != null){ return true; } if(steps >= 100){ winner = Thread.currentThread().getName(); System.out.println("winner is "+winner); } return false; } } //乌龟 class Tortoise implements Runnable{ @Override public void run() { for (int i = 1; i <= 100; ) { boolean flag = TortoiseAndHareRace.gameOver(i); if(flag){ break; } System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); i++; } } } //兔子 class Hare implements Runnable{ @Override public void run() { for (int i = 2; i <= 100; ) { boolean flag = TortoiseAndHareRace.gameOver(i); if(flag){ break; } if(i % 20 == 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); i+=2; } } }
运行结果:
1.实现Collable接口,需要返回值类型
2.重写Call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
5.提交执行:Future result1 = ser.submit(t1);
6.获取结果:boolean r1 = result1.get();
7.关闭服务:ser.shutdownNow();
package thread; import java.util.concurrent.*; //线程创建方式三:实现Callable接口 /* Cllable的好处: 1.可以定义返回值 2.可以抛出异常 */ 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 { WebDownload webDownload = new WebDownload(); webDownload.downloader(url,name); System.out.println("下载了文件名为:"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { String url1 = "https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=%E8%B7%AF%E9%A3%9E&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=3765507157,583221466&os=763603878,172912997&simid=3765507157,583221466&pn=37&rn=1&di=135190&ln=1650&fr=&fmq=1640444510388_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fi0.hdslb.com%252Fbfs%252Farticle%252Fb224524798157bf6a1a930c36900d07cec8e2ef6.jpg%26refer%3Dhttp%253A%252F%252Fi0.hdslb.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036513%26t%3D4f472c2960c9327a57f6b01bba6b3a90&rpstart=0&rpnum=0&adpicid=0&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D&ctd=1640444689019^3_1579X621%1"; String url2 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E8%89%BE%E6%96%AF&step_word=&hs=0&pn=10&spn=0&di=86460&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=219613496%2C2423385425&os=244226930%2C1703390822&simid=219613496%2C2423385425&adpicid=0&lpn=0&ln=1593&fr=&fmq=1640444879402_R&fm=result&ic=&s=undefined&hd=&latest=©right=&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F23%2F20210723140554_8a24f.thumb.1000_0.png%26refer%3Dhttp%3A%2F%2Fc-ssl.duitang.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036879%26t%3D573104878cca8c90c93593482068ae3b&gsm=1e&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D"; String url3 = "https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E8%90%A8%E5%8D%9A&step_word=&hs=0&pn=34&spn=0&di=20020&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=1538742093%2C620552439&os=1291420748%2C3189193177&simid=3181336095%2C3605445912&adpicid=0&lpn=0&ln=1637&fr=&fmq=1640444809527_R&fm=result&ic=&s=undefined&hd=&latest=©right=&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201612%2F25%2F20161225161314_fUCsM.thumb.400_0.jpeg%26refer%3Dhttp%3A%2F%2Fc-ssl.duitang.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1643036858%26t%3D0e17573db0c86d58a5545185cb5edf17&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B17tpwg2_z%26e3Bv54AzdH3Fwsk74AzdH3F%3Ft1%3Dldllanda%26fr4%3Dda89_z%26e3B8dccnmbb_z%26e3Bdad_z%26e3Ba&gsm=23&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMSw1LDQsOCw3LDIsOQ%3D%3D"; TestCallable t1 = new TestCallable(url1,"路飞.jpg"); TestCallable t2 = new TestCallable(url2,"艾斯.jpg"); TestCallable t3 = new TestCallable(url3,"萨博.jpg"); //创建执行服务 需要创建三个线程池 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //获取结果 boolean res1 = r1.get(); boolean res2 = r2.get(); boolean res3 = r3.get(); System.out.println(res1); System.out.println(res2); System.out.println(res3); //关闭服务 ser.shutdownNow(); } }
package proxy; //静态代理需要代理类和被代理类实现同一接口,将一些行为统一包装在代理类里,被代理类只需要"调用"即可 /* 静态代理模式总结: 真实对象和代理对象都需要实现统一接口 代理对象要代理真实对象 好处: 代理对象可以做很多真实对象做不到的事情 真实对象专注做自己的事情 */ public class StaticProxy { public static void main(String[] args) { WeddingComoany weddingComoany = new WeddingComoany(new You()); weddingComoany.HappyMarry(); } } interface Marry{ void HappyMarry(); } //真实角色,你去结婚 class You implements Marry{ @Override public void HappyMarry() { System.out.println("阿诚结婚超开心!!"); } } //代理角色,帮助你结婚 class WeddingComoany implements Marry{ //代理谁 ==> 真实目标角色 private Marry target; public WeddingComoany(Marry target) { this.target = target; } @Override public void HappyMarry() { before(); this.target.HappyMarry();//这就是真实对象 after(); } private void before() { System.out.println("结婚之前,布置现场"); } private void after() { System.out.println("结婚之后,收尾款"); } }
注意:静态代理与Thread
package lambda; //推导lambda表达式 public class TestLambda { public static void main(String[] args) { ILike like = new Like(); like.lambda(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("I like lambda"); } }
package lambda; //推导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(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("I like lambda"); } }
package lambda; //推导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(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("I like lambda"); } }
package lambda; //推导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(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("I like lambda"); } }
package lambda; //推导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 lambda5"); }; like.lambda(); } } //1.定义一个函数式接口 interface ILike{ void lambda(); } //2.实现类 class Like implements ILike{ @Override public void lambda() { System.out.println("I like lambda"); } }
package lambda; public class TestLambda2 { //2.静态内部类 static class Love2 implements ILove{ @Override public void love(int n) { System.out.println("I Love You "+n+"号"); } } public static void main(String[] args) { ILove l = new Love1(); l.love(1); l = new Love2(); l.love(2); //3.局部内部类 class Love3 implements ILove{ @Override public void love(int n) { System.out.println("I Love You "+n+"号"); } } l = new Love3(); l.love(3); //3.匿名内部类 l = new ILove(){ @Override public void love(int n) { System.out.println("I Love You "+n+"号"); } }; l.love(4); //lambda表达式 l = (int n) ->{ System.out.println("I Love You "+n+"号"); }; l.love(5); //lambda简化1:去掉参数类型 l = (n) ->{ System.out.println("I Love You "+n+"号"); }; l.love(520); //lambda简化2:简化括号 l = n ->{ System.out.println("I Love You "+n+"号"); }; l.love(521); //lambda简化3:简化花括号 l = n ->System.out.println("I Love You "+n+"号"); l.love(233); } } interface ILove{ void love(int n); } //1.外部类 class Love1 implements ILove{ @Override public void love(int n) { System.out.println("I Love You "+n+"号"); } }
1.lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹
2.前提是函数式接口
3.多个参数也可以去掉类型,要去掉都去掉,必须加括号
package thread; //测试stop //1.建议线程正常停止 --> 利用次数,不建议死循环 //2.建议使用标志位 --> 设置一个标志位 //3.不要使用stop或者destory等过时或者JDK不建议使用的方法 public class TestStop implements Runnable{ //1.设置一个标识位 private boolean flag = true; @Override public void run() { int i = 0; while (flag){ System.out.println("run...Thread"+i++); } } //2.设置一个公开的方法停止线程,转换标志位 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 < 1000; i++) { System.out.println("main "+i); if(i == 900){ //调用stop方法切换标志位,让线程停止 testStop.stop(); System.out.println("线程该停止了"); } } } }
例子一:
package thread; //模拟网络延时:放大问题的发生性 public class TestSleep implements Runnable{ //票数 private int ticketNums = 10; @Override public void run() { while (ticketNums > 0){ //模拟延时 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { TestSleep ticket = new TestSleep(); new Thread(ticket,"小程").start(); new Thread(ticket,"小白").start(); new Thread(ticket,"小黑").start(); } }
例子二:
package thread; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.SimpleFormatter; //模拟倒计时... //计时器可能不准 cpu调度也需要时间 public class TestSleep2 { public static void main(String[] args) { /*try { tenDown(); } catch (InterruptedException e) { e.printStackTrace(); }*/ //打印当前系统时间 Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间 while(true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis());//更新当前时间 } catch (InterruptedException e) { e.printStackTrace(); } } } //模拟倒计时 public static void tenDown() throws InterruptedException { int num = 10; while (true){ Thread.sleep(1000); System.out.println(num--); if(num <= 0){ break; } } } }
package thread; public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"A").start(); new Thread(myYield,"B").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield();//礼让 System.out.println(Thread.currentThread().getName()+"线程停止执行"); } }
package thread; //测试join方法 想象为插队 public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("线程vip来了 "+i); } } 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); } } }
package thread; //观察测试线程的状态 //主线程观测子线程状态,子线程run()方法跑完子线程终止 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(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("----------"); }); //观察状态 Thread.State state = thread.getState(); System.out.println(state);//NEW //观察启动后的状态 thread.start();//启动线程 state = thread.getState(); System.out.println(state);//RUN while (state != Thread.State.TERMINATED){//只要线程一直不终止,就一直输出状态 Thread.sleep(100); state = thread.getState();//更新线程状态 System.out.println(state);//输出状态 } //死亡之后的线程不能再次启动 thread.start(); } }
例子:
用户进程:main()
守护进程:gc()
package thread; //测试守护进程 //上帝守护你 public class TestDaemon{ public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true);//默认是false表示用户线程,正常的线程都是用户线程... thread.start();//上帝守护线程启动 new Thread(you).start();//你 用户线程启动... } } //上帝 class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑着你"); } } } //你 class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("你一生都很开心"); } System.out.println("=====goodbye world!====="); } }
多个线程操作同一资源
并发:同一个对象被多个线程同时操作
形成条件:队列 + 锁
package syn; import basic.B; //不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"A").start(); new Thread(station,"B").start(); new Thread(station,"C").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums = 10; boolean flag = true;//外部停止方式 @Override public void run() { //买票 while(flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { //判断是否有票 if(ticketNums<=0){ flag = false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
package syn; import basic.A; //不安全的取钱 //两个人去银行取钱,账户 public class UnsafeBank { public static void main(String[] args) { //账户 Account account = new Account(100,"结婚基金"); Withdraw you = new Withdraw(account,50,"你"); Withdraw girlfriend = new Withdraw(account,100,"女朋友"); you.start(); girlfriend.start(); } } //账户 class Account{ int money;//余额 String name;//卡号 public Account(int money, String name) { this.money = money; this.name = name; } } //银行:模拟取款 class Withdraw extends Thread{ Account account;//账户 int withdrawMoney;//取多少钱 int nowMoney;//现在手里有多少钱 public Withdraw(Account account,int withdrawMoney,String name){ super(name); this.account = account; this.withdrawMoney = withdrawMoney; } //取钱 @Override public void run() { //判断有没有钱 if (account.money - withdrawMoney < 0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 = 余额 - 你取的钱; account.money = account.money - withdrawMoney; //你手里的钱 nowMoney = nowMoney + withdrawMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread().getName() == this.getName() //Withdraw继承Thread,Thread有getName()方法 System.out.println(this.getName()+"手里的钱"+nowMoney); } }
package syn; import java.util.ArrayList; import java.util.List; //线程不安全的集合 //同一瞬间把两个线程添加到了同一位置 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
同一瞬间把两个线程添加到了同一位置
package syn; import basic.B; //不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"A").start(); new Thread(station,"B").start(); new Thread(station,"C").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums = 10; boolean flag = true;//外部停止方式 @Override public void run() { //买票 while(flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //synchronized 同步方法,锁的是this private synchronized void buy() throws InterruptedException { //判断是否有票 if(ticketNums<=0){ flag = false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
package syn; import basic.A; //不安全的取钱 //两个人去银行取钱,账户 public class UnsafeBank { public static void main(String[] args) { //账户 Account account = new Account(100,"结婚基金"); Withdraw you = new Withdraw(account,50,"你"); Withdraw girlfriend = new Withdraw(account,100,"女朋友"); you.start(); girlfriend.start(); } } //账户 class Account{ int money;//余额 String name;//卡号 public Account(int money, String name) { this.money = money; this.name = name; } } //银行:模拟取款 class Withdraw extends Thread{ Account account;//账户 int withdrawMoney;//取多少钱 int nowMoney;//现在手里有多少钱 public Withdraw(Account account,int withdrawMoney,String name){ super(name); this.account = account; this.withdrawMoney = withdrawMoney; } //取钱 //synchronized默认锁的是this @Override public void run() { synchronized (account){ //判断有没有钱 if (account.money - withdrawMoney < 0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 = 余额 - 你取的钱; account.money = account.money - withdrawMoney; //你手里的钱 nowMoney = nowMoney + withdrawMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread().getName() == this.getName() //Withdraw继承Thread,Thread有getName()方法 System.out.println(this.getName()+"手里的钱"+nowMoney); } } }
package syn; import java.util.ArrayList; import java.util.List; //线程不安全的集合 //同一瞬间把两个线程添加到了同一位置 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
package syn; import java.util.concurrent.CopyOnWriteArrayList; //测试JUC安全类型的集合 public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } //主线程等待其他线程执行完毕 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
//死锁:多个线程相互抱着对方需要的资源,然后形成僵持 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;//使用化妆品的人 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+"获得了镜子的锁"); }*/ } 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+"获得了口红的锁"); }*/ } synchronized (lipstick){ System.out.println(this.girlName+"获得了口红的锁"); } } } }
import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); } } class TestLock2 implements Runnable{ int ticketsNum = 10; //定义lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock();//加锁 if (ticketsNum > 0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketsNum--); }else { break; } }finally { //解锁 lock.unlock(); } } } }