Java线程
Java线程
1、线程简介
进程和线程是两基本的单元被执行,相比较来说在并发程序中更多的是关注的JAVA线程。
Java多线程就是多个任务。
进程
一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用,在一个程序中包含多个进程。JAVA运行环境中作为一个进程包含不同的类和程序流程。
线程
线程可以被叫做轻量级的进程,线程需要更少的资源来创建和存在在这个过程中,线程共享进程的资源。 每个java应用程序有至少一个线程-(main方法主线程),虽然在后台有很多其它java 线程运行例如内存管理,系统管理和单进程等,但是对于应用程序来说-主线程是第一个Java线程并且我们在其中可以创建多线程。多线程是指两个或多个线程同时执行在一个程序中,在单核处理器的计算机中一次只能执行一个线程,操作系统中存在一个“时间切片” 特性用于切分处理器时间在不同的进程和线程中。
Java线程的优势
1.Java线程是轻量级的相比进程来说,当创建一个线程是可以花费少的时间和资源。
2.线程共享它们父进程的数据和代码。
3.上下文切换中通常线程比进程代价更少。
4.线程的内部通讯中是相对容易的比进程通讯。
**Java提供两种方式编写创建线程
1.实现 the java.lang.Runnable 接口.**
确保这个类是可以运行的,我们可以实现java.lang.Runnable中的public void run()方法,为了使得类作为一个线程,我们需要创建一个线程对象,通过这个实例化的对象,然后调用start()方法为了来执行run()方法在一个单独的线程中。
这是一个 java thread 举例 通过实现 Runnable 接口.
package com.journaldev.threads; public class HeavyWorkRunnable implements Runnable { @Override public void run() { System.out.println("Doing heavy processing - START "+Thread.currentThread().getName()); try { Thread.sleep(1000); //Get database connection, delete unused data from DB doDBProcessing(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Doing heavy processing - END "+Thread.currentThread().getName()); } private void doDBProcessing() throws InterruptedException { Thread.sleep(5000); } }
2.继承 the java.lang.Thread 类.
我们能够继承java.lang.Thread类为了创建我们自己java线程类并且重写run()方法,同时我们能够创建这个对象和调用start()方法为了执行我们自定义的java线程类run()方法。
这是一个 java thread 举例说明了如何继承Thread类
package com.journaldev.threads; public class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { System.out.println("MyThread - START "+Thread.currentThread().getName()); try { Thread.sleep(1000); //Get database connection, delete unused data from DB doDBProcessing(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("MyThread - END "+Thread.currentThread().getName()); } private void doDBProcessing() throws InterruptedException { Thread.sleep(5000); } }
小结
- 线程就是独立执行路径;
- 在程序运行时,即使没有自己创建线程,后台也会有对个线程,如主线程,gc线程;
- main() 称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程当中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的;
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如CPU调度时间,并发控制开销;
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
2、线程创建
- Thread
- Runnable
- Callable
Thread
package com.q; /* * 创建线程方式一 * 继承Thread * 重写run()方法 * 调用Start()方法,开启线程 * * 测试作用: * 可以看出线程开启不一定立即执行,由CPU调度执行*/ public class TestThread extends Thread{ @Override public void run() { for (int i=0;i<=1000;i++){ System.out.println(i+" 我在看代码 thread"); } } public static void main(String[] args) { TestThread testThread = new TestThread(); testThread.start(); //testThread.run(); for (int i=0;i<=10;i++){ System.out.println(i+" **** thread+++++"); } } }
下载图片实现
package com.q; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; 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() { WebDownLoad webDownLoadm = new WebDownLoad(); webDownLoadm.downloader(url,name); System.out.println(url + " "+name); } public static void main(String[] args) { TestThread2 testThread1 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016", "中秋1.png"); TestThread2 testThread2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016", "中秋2.png"); TestThread2 testThread3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016", "中秋3.png"); testThread1.start(); testThread2.start(); testThread3.start(); } } /* * 下载图片类 * */ class WebDownLoad{ 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方法出现问题"); } } }
Runnable
package com.q.runable; import com.q.thread.TestThread; /* * 创建线程方式2 * 实现Runnable接口 * 重写run()方法 * 执行线程需要丢入Runnable接口的实现类,调用start方法 * */ public class TestRunnable implements Runnable{ @Override public void run() { for (int i=0;i<=100;i++){ System.out.println(i+" 我在看代码 thread"); } } public static void main(String[] args) { //创建Runnable接口的实现类 TestRunnable testRunnable = new TestRunnable(); //创建线程对象,通过线程对象来开启线程,代理 Thread thread = new Thread(testRunnable); //开启线程 thread.start(); for (int i=0;i<=1000;i++){ System.out.println(i+" **** thread+++++"); } } }
模拟购票
代码
package com.q.并发; public class Ticket implements Runnable{ private Integer ticketNum= 20; @Override public void run() { while(true){ if(ticketNum<=0){ break; } //测试 if (ticketNum.equals(0)){ break; } System.out.println(Thread.currentThread().getName()+"--拿到了第"+ticketNum--+"张票"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { //线程对象 Ticket ticket = new Ticket(); //启动线程 new Thread(ticket,"青岚").start(); new Thread(ticket,"卿云").start(); new Thread(ticket,"飞羽").start(); } }
运行结果
青岚--拿到了第20张票 飞羽--拿到了第18张票 卿云--拿到了第19张票 青岚--拿到了第17张票 飞羽--拿到了第16张票 卿云--拿到了第15张票 青岚--拿到了第14张票 卿云--拿到了第13张票 飞羽--拿到了第13张票 青岚--拿到了第12张票 卿云--拿到了第11张票 飞羽--拿到了第10张票 青岚--拿到了第9张票 卿云--拿到了第8张票 飞羽--拿到了第7张票 青岚--拿到了第6张票 卿云--拿到了第5张票 飞羽--拿到了第4张票 卿云--拿到了第3张票 青岚--拿到了第2张票 飞羽--拿到了第1张票 Process finished with exit code 0
3、案列-模拟龟兔赛跑
代码(一版)
package com.q.龟兔; public class Rose implements Runnable{ private static String winner; @Override public void run() { for (int i=0;i<=100;i++){ boolean flag= gameOver(i); if(flag){ break; } System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步"); } } private boolean gameOver(Integer steps){ if(winner!=null){ return true; } else{ if (steps>=100){ winner = Thread.currentThread().getName(); System.out.println("胜利者是:"+winner); return false; } } return false; } public static void main(String[] args) { Rose rose = new Rose(); new Thread(rose,"乌龟").start(); new Thread(rose,"兔子").start(); } }
结果
乌龟-->跑了0步 兔子-->跑了0步 乌龟-->跑了1步 兔子-->跑了1步 乌龟-->跑了2步 兔子-->跑了2步 乌龟-->跑了3步 兔子-->跑了3步 乌龟-->跑了4步 兔子-->跑了4步 乌龟-->跑了5步 兔子-->跑了5步 乌龟-->跑了6步 兔子-->跑了6步 乌龟-->跑了7步 兔子-->跑了7步 乌龟-->跑了8步 兔子-->跑了8步 乌龟-->跑了9步 兔子-->跑了9步 乌龟-->跑了10步 兔子-->跑了10步 乌龟-->跑了11步 乌龟-->跑了12步 兔子-->跑了11步 兔子-->跑了12步 兔子-->跑了13步 乌龟-->跑了13步 乌龟-->跑了14步 兔子-->跑了14步 乌龟-->跑了15步 兔子-->跑了15步 兔子-->跑了16步 兔子-->跑了17步 兔子-->跑了18步 兔子-->跑了19步 兔子-->跑了20步 兔子-->跑了21步 兔子-->跑了22步 兔子-->跑了23步 兔子-->跑了24步 兔子-->跑了25步 兔子-->跑了26步 兔子-->跑了27步 兔子-->跑了28步 兔子-->跑了29步 兔子-->跑了30步 乌龟-->跑了16步 乌龟-->跑了17步 乌龟-->跑了18步 兔子-->跑了31步 乌龟-->跑了19步 兔子-->跑了32步 乌龟-->跑了20步 兔子-->跑了33步 乌龟-->跑了21步 兔子-->跑了34步 乌龟-->跑了22步 兔子-->跑了35步 乌龟-->跑了23步 兔子-->跑了36步 乌龟-->跑了24步 兔子-->跑了37步 乌龟-->跑了25步 兔子-->跑了38步 乌龟-->跑了26步 兔子-->跑了39步 乌龟-->跑了27步 兔子-->跑了40步 乌龟-->跑了28步 兔子-->跑了41步 兔子-->跑了42步 兔子-->跑了43步 兔子-->跑了44步 兔子-->跑了45步 兔子-->跑了46步 兔子-->跑了47步 兔子-->跑了48步 兔子-->跑了49步 兔子-->跑了50步 兔子-->跑了51步 兔子-->跑了52步 兔子-->跑了53步 乌龟-->跑了29步 兔子-->跑了54步 兔子-->跑了55步 乌龟-->跑了30步 乌龟-->跑了31步 兔子-->跑了56步 乌龟-->跑了32步 兔子-->跑了57步 兔子-->跑了58步 兔子-->跑了59步 乌龟-->跑了33步 兔子-->跑了60步 兔子-->跑了61步 兔子-->跑了62步 兔子-->跑了63步 兔子-->跑了64步 乌龟-->跑了34步 兔子-->跑了65步 乌龟-->跑了35步 兔子-->跑了66步 乌龟-->跑了36步 兔子-->跑了67步 乌龟-->跑了37步 兔子-->跑了68步 乌龟-->跑了38步 兔子-->跑了69步 乌龟-->跑了39步 兔子-->跑了70步 乌龟-->跑了40步 兔子-->跑了71步 乌龟-->跑了41步 兔子-->跑了72步 乌龟-->跑了42步 兔子-->跑了73步 乌龟-->跑了43步 兔子-->跑了74步 乌龟-->跑了44步 兔子-->跑了75步 乌龟-->跑了45步 兔子-->跑了76步 乌龟-->跑了46步 兔子-->跑了77步 乌龟-->跑了47步 兔子-->跑了78步 乌龟-->跑了48步 兔子-->跑了79步 乌龟-->跑了49步 兔子-->跑了80步 乌龟-->跑了50步 兔子-->跑了81步 乌龟-->跑了51步 兔子-->跑了82步 乌龟-->跑了52步 兔子-->跑了83步 乌龟-->跑了53步 兔子-->跑了84步 乌龟-->跑了54步 兔子-->跑了85步 乌龟-->跑了55步 兔子-->跑了86步 乌龟-->跑了56步 兔子-->跑了87步 乌龟-->跑了57步 乌龟-->跑了58步 乌龟-->跑了59步 兔子-->跑了88步 乌龟-->跑了60步 兔子-->跑了89步 乌龟-->跑了61步 兔子-->跑了90步 乌龟-->跑了62步 兔子-->跑了91步 乌龟-->跑了63步 兔子-->跑了92步 乌龟-->跑了64步 兔子-->跑了93步 乌龟-->跑了65步 兔子-->跑了94步 乌龟-->跑了66步 兔子-->跑了95步 乌龟-->跑了67步 兔子-->跑了96步 乌龟-->跑了68步 兔子-->跑了97步 乌龟-->跑了69步 兔子-->跑了98步 乌龟-->跑了70步 兔子-->跑了99步 乌龟-->跑了71步 胜利者是:兔子 兔子-->跑了100步 Process finished with exit code 0
升级版(兔子休眠)
package com.q.龟兔; public class Rose implements Runnable{ 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(1); } catch (InterruptedException e) { e.printStackTrace(); } } boolean flag= gameOver(i); if(flag){ break; } System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步"); } } private boolean gameOver(Integer steps){ if(winner!=null){ return true; } else{ if (steps>=100){ winner = Thread.currentThread().getName(); System.out.println("胜利者是:"+winner); return false; } } return false; } public static void main(String[] args) { Rose rose = new Rose(); new Thread(rose,"乌龟").start(); new Thread(rose,"兔子").start(); } }
结果
乌龟-->跑了0步 乌龟-->跑了1步 乌龟-->跑了2步 乌龟-->跑了3步 乌龟-->跑了4步 乌龟-->跑了5步 乌龟-->跑了6步 乌龟-->跑了7步 兔子-->跑了0步 乌龟-->跑了8步 兔子-->跑了1步 乌龟-->跑了9步 兔子-->跑了2步 乌龟-->跑了10步 兔子-->跑了3步 乌龟-->跑了11步 兔子-->跑了4步 乌龟-->跑了12步 兔子-->跑了5步 乌龟-->跑了13步 兔子-->跑了6步 乌龟-->跑了14步 兔子-->跑了7步 乌龟-->跑了15步 兔子-->跑了8步 乌龟-->跑了16步 兔子-->跑了9步 乌龟-->跑了17步 兔子-->跑了10步 乌龟-->跑了18步 兔子-->跑了11步 乌龟-->跑了19步 兔子-->跑了12步 乌龟-->跑了20步 兔子-->跑了13步 乌龟-->跑了21步 兔子-->跑了14步 乌龟-->跑了22步 兔子-->跑了15步 乌龟-->跑了23步 兔子-->跑了16步 乌龟-->跑了24步 兔子-->跑了17步 乌龟-->跑了25步 兔子-->跑了18步 乌龟-->跑了26步 兔子-->跑了19步 乌龟-->跑了27步 乌龟-->跑了28步 乌龟-->跑了29步 乌龟-->跑了30步 乌龟-->跑了31步 乌龟-->跑了32步 乌龟-->跑了33步 乌龟-->跑了34步 乌龟-->跑了35步 乌龟-->跑了36步 乌龟-->跑了37步 乌龟-->跑了38步 乌龟-->跑了39步 乌龟-->跑了40步 乌龟-->跑了41步 乌龟-->跑了42步 乌龟-->跑了43步 乌龟-->跑了44步 乌龟-->跑了45步 乌龟-->跑了46步 乌龟-->跑了47步 乌龟-->跑了48步 乌龟-->跑了49步 乌龟-->跑了50步 乌龟-->跑了51步 乌龟-->跑了52步 乌龟-->跑了53步 乌龟-->跑了54步 乌龟-->跑了55步 乌龟-->跑了56步 乌龟-->跑了57步 乌龟-->跑了58步 乌龟-->跑了59步 乌龟-->跑了60步 乌龟-->跑了61步 乌龟-->跑了62步 乌龟-->跑了63步 乌龟-->跑了64步 乌龟-->跑了65步 乌龟-->跑了66步 乌龟-->跑了67步 乌龟-->跑了68步 乌龟-->跑了69步 乌龟-->跑了70步 乌龟-->跑了71步 乌龟-->跑了72步 乌龟-->跑了73步 乌龟-->跑了74步 乌龟-->跑了75步 乌龟-->跑了76步 乌龟-->跑了77步 乌龟-->跑了78步 乌龟-->跑了79步 乌龟-->跑了80步 乌龟-->跑了81步 乌龟-->跑了82步 乌龟-->跑了83步 乌龟-->跑了84步 乌龟-->跑了85步 乌龟-->跑了86步 乌龟-->跑了87步 兔子-->跑了20步 乌龟-->跑了88步 兔子-->跑了21步 乌龟-->跑了89步 兔子-->跑了22步 乌龟-->跑了90步 兔子-->跑了23步 乌龟-->跑了91步 兔子-->跑了24步 乌龟-->跑了92步 兔子-->跑了25步 乌龟-->跑了93步 兔子-->跑了26步 乌龟-->跑了94步 兔子-->跑了27步 乌龟-->跑了95步 兔子-->跑了28步 乌龟-->跑了96步 兔子-->跑了29步 乌龟-->跑了97步 兔子-->跑了30步 乌龟-->跑了98步 兔子-->跑了31步 乌龟-->跑了99步 兔子-->跑了32步 胜利者是:乌龟 乌龟-->跑了100步 Process finished with exit code 0
4、Callable接口
介绍
多线程之callable详解
面试有人会问:线程的实现方式有几种?
很多人可能回答:2种,继承Thread类,实现Runnable接口。
很多忽略了callable这种方式。
也许有人知道callable,也知道callable和Runnable的区别是callable可以有返回值,也可以抛出异常的特性,而Runnable没有。
这里估计很多人懵逼,接下来我们就从源码层次讲解这个问题。
注意callable可以有返回值,也可以抛出异常这点很关键。
很多时候我们让多线程去帮我们处理事情,是需要拿到返回值的,有了异常也可以处理,比如某宝的APP页面,一个页面展示3个块,而每个块展示的信息从后端获取的接口都不一样,那么是让前端调后端3次接口吗?
肯定不行,后端可以把3个块的信息,包装成一个接口,全部返回,那么问题来了,后端调用3个接口,比如第一个接口需要1秒,第二个需要2秒,第三个需要3秒,那么包装的这个接口响应时间最少6秒,怎么解决这个问题呢,可以用多线程来帮我们解决。
启动3个线程,每个线程去调用一个接口,那么3个线程一共执行完的时间就是最慢的那个线程的执行时间,这样接口的响应时间就变成了3秒,一下节省了一半的时间。
那么问题来了,线程如何把执行的业务代码的结果返回来呢?这时候就用到callable了。
实现Callable接口
- 实现Callable接口,需要返回值类型;
- 重写Call()方法,需要抛出异常;
- 创建目标对象;
- 创建执行服务:
- 提交执行:
- 获取结果:
- 关闭服务:
代码
package com.q.Call; import com.q.thread.TestThread2; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.PreparedStatement; import java.util.concurrent.*; import static java.util.concurrent.Executors.*; /* * 实现线程方式三 * 编写图片下载器 * */ 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 webDownLoadm = new WebDownLoad(); webDownLoadm.downloader(url,name); System.out.println(url + " "+name); return true; } public static void main(String[] args) { TestCallable c1 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016", "中秋4.png"); TestCallable c2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016", "中秋5.png"); TestCallable c3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016", "中秋6.png"); //创建执行服务 ExecutorService service = newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = service.submit(c1); Future<Boolean> r2 = service.submit(c2); Future<Boolean> r3 = service.submit(c3); //获取结果 try { boolean b1 = r1.get(); boolean b2 = r2.get(); boolean b3 = r3.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } //关闭服务 service.shutdown(); } } /* * 下载图片类 * */ class WebDownLoad{ 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方法出现问题"); } } }
结果
https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016 中秋6.png https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016 中秋5.png https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634566265&t=7344ed1c5f3b20fccda1387f1256f016 中秋4.png Process finished with exit code 0
5、线程的静态代理
演示
- 你:真实角色
- 婚庆公司:代理你,处理结婚相关事宜
- 结婚:实现结婚接口即可
代码
package com.q.staticProxy; public class StaticProxy { public static void main(String[] args) { WeddingCompany weddingCompany = new WeddingCompany(new You()); weddingCompany.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("结婚之前,布置现场"); } }
分析
代理模式中真实角色与代理角色都必须实现接口;
代理角色处理真实目标角色的要求
自己要做的是交给代理,由代理完成,真实角色是参与者,代理公司是行动者。
6、Lamda表达式
Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)
Lambda表达式是JAVA8中提供的一种新的特性,它支持JAVA也能进行简单的“函数式编程”。
它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
传统的类结构:
package com.q.lamda; /* * 推导Lamda表达式 * */ public class TestLamda { public static void main(String[] args) { LamdaLike lamdaLike = new Like(); lamdaLike.lamda(); } } /* * 定义一个函数接口 * */ interface LamdaLike{ void lamda(); } class Like implements LamdaLike{ @Override public void lamda() { System.out.println("I am a Lamda "); } }
升级版:
package com.q.lamda; /* * 推导Lamda表达式 * */ public class TestLamda { /* //定义静态内部类 * */ static class Like2 implements LamdaLike{ @Override public void lamda() { System.out.println("I am a Lamda2"); } } public static void main(String[] args) { LamdaLike lamdaLike = new Like(); lamdaLike.lamda(); lamdaLike = new Like2(); lamdaLike.lamda(); /*\ * 局部类 * */ class Like3 implements LamdaLike{ @Override public void lamda() { System.out.println("I am a Lamda3"); } } lamdaLike = new Like3(); lamdaLike.lamda(); /* * 匿名内部类 * 必须借助接口或者父类 * */ lamdaLike = new Like(){ @Override public void lamda() { System.out.println("I am a Lamda4"); } }; lamdaLike.lamda(); /* * Lamdba 简化 * */ lamdaLike = ()->{ System.out.println("I am a Lamdba5"); }; lamdaLike.lamda(); } } /* * 定义一个函数接口 * */ interface LamdaLike{ void lamda(); } class Like implements LamdaLike{ @Override public void lamda() { System.out.println("I am a Lamda "); } }
避免代码过多,过多的匿名内部类;
案列(带参数)
package com.q.lamda; public class TestLamdba { static class Love1 implements LamdbaLove{ @Override public void love(String x, String y) { System.out.println(x+"@@"+y); } } public static void main(String[] args) { LamdbaLove love = new Love(); love.love("青岚","卿云"); love = new Love1(); love.love("青岚","卿云"); love = new LamdbaLove() { @Override public void love(String x, String y) { System.out.println(x+"@@@"+y+"匿名内部类"); } }; love.love("青岚","卿云"); love = (String x,String y)->{ System.out.println(x+"@@@"+y+"Lamdba表达式"); }; love.love("青岚","卿云"); /* * 去掉参数类型 * */ LamdbaLove love1 = (x,y)->{ System.out.println(x+"@@@"+y+"Lamdba表达式"); }; love1.love("青岚","卿云"); } } interface LamdbaLove{ void love(String x,String y); } class Love implements LamdbaLove{ @Override public void love(String x, String y) { System.out.println(x+"@"+y); } }
7、线程状态
线程有五个状态
线程停止
package com.q.线程状态; /* * 线程测试stop * 建议线程自动停止,---》利用次数,不建议死循环 * 不使用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(); Thread thread = new Thread(testStop); thread.start(); for (int i=0;i<1000;i++){ System.out.println(i); if( i==900 ){ //调用stop方法切换标志位,停止线程 testStop.stop(); System.out.println("停止线程"); break; } } } }
线程休眠
package com.q.线程状态; import java.text.SimpleDateFormat; import java.util.Date; /* * 模拟倒计时 * */ public class TestSleep { public static void main(String[] args) { 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(){ int num = 10; while(true){ try { Thread.sleep(1000); System.out.println(num); num--; } catch (InterruptedException e) { e.printStackTrace(); } if(num==0){ break; } } } }
线程礼让
yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!看CPU心情
package com.q.线程状态; /* * 测试礼让线程 * */ public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"卿云").start(); new Thread(myYield,"青岚").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"线程停止执行"); } }
线程强制执行
package com.q.线程状态; import org.junit.rules.TestRule; public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程VIP来了"+i); } } public static void main(String[] args) { TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 50; i++) { if(i==30){ try { thread.join(); System.out.println("------------------"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("main()"+i); } } }
观测线程状态
package com.q.线程状态; public class TestState { public static void main(String[] args) throws InterruptedException { Runnable target; Thread thread = new Thread(()->{ for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("*****************"); } }); //观察状态 Thread.State state = thread.getState(); System.out.println(state); //观察启动后 thread.start(); state = thread.getState(); System.out.println(state); while (state !=Thread.State.TERMINATED){ Thread.sleep(100); state = thread.getState(); System.out.println(state); } } }
结果:
NEW RUNNABLE TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING ***************** TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING ***************** TERMINATED
8、线程的优先级
package com.q.priority; public class TestPriority { public static void main(String[] args) { //主线程优先级 System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); Thread t5 = new Thread(myPriority); Thread t6 = new Thread(myPriority); //设置线程优先级 //先设置优先级,在启动 t1.start(); t2.setPriority(4); t3.setPriority(5); t3.start(); t4.start(); t5.setPriority(3); t5.start(); t6.setPriority(Thread.MAX_PRIORITY); t6.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); } }
9、守护线程
daemon
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待
10、线程同步机制
并发: 同一个对象被多个线程同时操作
要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博文中的例子来说明,在多个线程之间共享了Count类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Count对象到工作内存中,然后执行代码count.increment(),改变了num值,最后用工作内存Count刷新主内存Count。当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值,此为可见性。多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性。本文讲述了JDK5.0之前传统线程的同步方式,更高级的同步方式可参见Java线程(八):锁对象Lock-同步问题更完美的处理方式。
每个锁对象(JLS中叫monitor)都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个线程被唤醒(notify)后,才会进入到就绪队列,等待CPU的调度,反之,当一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒,这个涉及到线程间的通信,下一篇博文会说明。看我们的例子,当第一个线程执行输出方法时,获得同步锁,执行输出方法,恰好此时第二个线程也要执行输出方法,但发现同步锁没有被释放,第二个线程就会进入就绪队列,等待锁被释放。一个线程执行互斥代码过程如下:
1.获得同步锁; 2. 清空工作内存; 3. 从主内存拷贝对象副本到工作内存; 4. 执行代码(计算或者输出等); 5. 刷新主内存数据; 6. 释放同步锁。
所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。
同步锁
ava 中的 synchronized 关键字可以在多线程环境下用来作为线程安全的同步锁。本文主要对 synchronized 的作用,以及其有效范围进行讨论。
Java中的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。
synchronized 关键字主要有以下几种用法:
- 非静态方法的同步;
- 静态方法的同步;
- 代码块。
下面分对象锁和类锁来分别说明 synchronized 用法:
对象锁
非静态方法使用 synchronized 修饰的写法,修饰实例方法时,锁定的是当前对象:
public synchronized void test(){ // TODO }
代码块使用 synchronized 修饰的写法,使用代码块,如果传入的参数是 this,那么锁定的也是当前的对象:
public void test(){ synchronized (this) { // TODO } }
下面通过例子来说明对象锁:
定义一个类,方法如下,将 count 自减,从 5 到 0:
public class TestSynchronized { public synchronized void minus() { int count = 5; for (int i = 0; i < 5; i++) { count--; System.out.println(Thread.currentThread().getName() + " - " + count); try { Thread.sleep(500); } catch (InterruptedException e) { } } } }
测试调用方法如下:
public class Run { public static void main(String[] args) { final TestSynchronized test = new TestSynchronized(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { test.minus(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { test.minus(); } }); thread1.start(); thread2.start(); } }
两个线程 thread1 和 thread2,同时访问对象的方法,由于该方法是 synchronized 关键字修饰的,那么这两个线程都需要获得该对象锁,一个获得后另一个线程必须等待。所以我们可以猜测运行结果应该是,一个线程执行完毕后,另一个线程才开始执行
案例(购票)
package syn; public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket buyTicket = new BuyTicket(); new Thread(buyTicket,"青岚").start(); new Thread(buyTicket,"卿云").start(); new Thread(buyTicket,"致非").start(); } } class BuyTicket implements Runnable{ private Integer ticketNum=10; boolean flag = true; @Override public void run() { //买票 while(flag){ try { Thread.sleep(100); buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private synchronized void buy(){ if (ticketNum<=0){ return; } System.out.println(Thread.currentThread().getName()+"-->拿到"+ticketNum--); } }
结果
卿云-->拿到10 青岚-->拿到9 致非-->拿到8 卿云-->拿到7 青岚-->拿到6 致非-->拿到5 卿云-->拿到4 青岚-->拿到3 致非-->拿到2 卿云-->拿到1
同步锁代码
package Lock; import java.util.List; /*死锁 * 多个线程互相拥有资源,对方却无法使用 * */ public class DeadLock { public static void main(String[] args) { MakeUp g1 = new MakeUp(1,"青岚"); MakeUp g2 = new MakeUp(0,"卿云"); g1.start(); g2.start(); } } class Lipstick{ } class Mirror { } class MakeUp extends Thread{ static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choise; String name; public MakeUp(int choise, String name) { this.choise = choise; this.name = name; } @Override public void run() { makeUp(); } //互相持有对方的锁 private void makeUp(){ if (choise==0){ synchronized (lipstick){//获得口红的锁 System.out.println("获得口红的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (mirror){ System.out.println("获得镜子的锁"); } }else { synchronized (mirror){//获得口红的锁 System.out.println("获得镜子的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (lipstick){ System.out.println("获得口红的锁"); } } } }
11、Lock 锁
12、线程协作
生产者与消费者模式
本文来自博客园,作者:致非,转载请注明原文链接:https://www.cnblogs.com/mycity/articles/thread.html
【推荐】AI 的力量,开发者的翅膀:欢迎使用 AI 原生开发工具 TRAE
【推荐】2025 HarmonyOS 鸿蒙创新赛正式启动,百万大奖等你挑战
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 源码浅析:SpringBoot main方法结束为什么程序不停止
· C#性能优化:为何 x * Math.Sqrt(x) 远胜 Math.Pow(x, 1.5)
· 本可避免的P1事故:Nginx变更导致网关请求均响应400
· 还在手写JSON调教大模型?.NET 9有新玩法
· 复杂业务系统线上问题排查过程
· AI 的力量,开发者的翅膀:欢迎使用字节旗下的 AI 原生开发工具 TRAE
· 「闲聊文」准大三的我,思前想后还是不搞java了
· .NET 9 的免费午餐:GZip 性能提升38.3%
· 2025年:是时候重新认识System.Text.Json了
· 开源新旗舰 GLM-4.5:不想刷榜,只想干活儿