java多线程设计模式-WorkerPattern
Java架构师必备知识点(高级程序员教程必备)
Java架构师必备知识点(高级程序员教程)2019年3月一、并发编程1.线程安全:当多个线程访问某一个类(对象)时这个类始终都能表现出正确的行为,那么这个类(对象和方法)就是线程安全的。
2.synchronized:可以在任意对象以及方法上加锁,而加锁的这段代码称为"互斥区"或者"临界区"。
一个线程想要执行synchronized修饰的方法里的内容,首先是尝试获得锁,如果拿到锁,执行synchronized方法体里面的内容如果拿不到那么这个线程会不断的尝试获得这把锁,直到拿到为止,而且是多个线程去竞争这把锁。
3.多个线程多个锁:多个线程,每个线程都将可以拿到自己指定的锁,分别获得锁之后,执行synchronized方法体的内容,关键字synchronized获得的锁都是对象锁,而不是把一段代码(方法)当做锁,在静态方法上机上synchronized获得的锁为类级别的锁,表示锁定类。
4.对象锁的同步和异步:同步synchronized:同步就是共享,同步的目的是为了线程安全,对于线程安全需要满足两个特性:原子性(同步)、可见性。
异步asynchronized:异步就是独立,相互之间不受任何制约。
5.脏读:对于对象的同步和异步方法,我们在设计程序的时候,一定要考虑问题的整体,不然就会出现数据不一致错误,很经典的错误就是脏读(dityread)。
在我们对一个对象的方法加锁的时候,需要考虑业务的整体性,即为setValue和getValue方法同时加锁synchronized同步关键字保证(service)业务逻辑层的原子性,不然会出现业务逻辑错误。
6.synchronized锁重入:关键字synchronized拥有重入锁的功能,也就是在使用synchronized时,当一个线程得到一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。
7.出现异常,锁自动释放:对于web应用程序,异常释放锁的情况,如果不及时处理,很可能对应用程序业务逻辑产生严重的错误。
《图解Java多线程设计模式》学习笔记(一)Java线程
《图解Java多线程设计模式》学习笔记(⼀)Java线程⼀、何谓线程1. 单线程程序处理流程始终如⼀条线某⼀时间点执⾏的处理只有⼀个正在执⾏程序的主体称为线程2. 多线程程序多个线程组成的程序称为多线程程序public class MyThread extends Thread{public void run(){for (int i = 0; i < 10000; i++) {System.out.println("Nice!");}}}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();// 注意要调⽤start,run⽅法可以调⽤,但不会开启新县城thread.start();for (int i = 0; i < 10000; i++) {System.out.println("Good!");}}}结果是交叉输出概念:顺序:多个操作依次处理并⾏:多个操作同时处理并发:将⼀个操作分成多个部分并允许⽆序处理⼆、线程的启动继承Thread,重写run⽅法public class PrintThread extends Thread {private String message;PrintThread(String message) {this.message = message;}public void run() {for (int i = 0; i < 10000; i++) {System.out.println(message);}}}public class Main {public static void main(String[] args) {new PrintThread("Good!").start();}}实现Runnable接⼝,实现run⽅法public class Printer implements Runnable {private String message;Printer(String message) {this.message = message;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println(message);}}}new Thread(new Printer("Nice!")).start();Thread本⾝实现了Runnable接⼝,且有run⽅法,⽅法体为空。
Java多线程之实现多线程的三种方法
Java多线程之实现多线程的三种⽅法⼀、创建多线程的⽅法1.继承Thread类类 Thread的类头为:public class Thread implement runnable继承Thread类,并重写Thread中的run⽅法例如:1package com.dragon.test;23public class MyThread extends Thread{4 @Override5 public void run(){6 System.out.println("创建多线程⽅法⼀");7 }8 public static void main(String[] args) {9 MyThread thread=new MyThread();10 thread.start();11 System.out.println("运⾏结束");12 }1314 }运⾏结果:这说明在使⽤多线程技术时,代码的运⾏结果与代码执⾏顺序后调⽤代码的顺序是⽆关的即线程是⼀个⼦任务,CPU以随机的时间来调⽤线程中的⽅法。
注意:1.不能多次调⽤Thread中的start()⽅法,否则会抛出IllegalThreadStateException异常。
2.启动线程的⽅法不是run()⽅法⽽是start⽅法,如果调⽤的是run()⽅法就是同步的,并不能异步执⾏。
3.执⾏start()⽅法的顺序不代表线程启动的顺序,即并不是说,越早调⽤某个线程的start()⽅法,它就能越早的执⾏其中的run()⽅法。
2.实现Runnable接⼝实现Runnable接⼝,重写run()⽅法例如:1package com.dragon.test;23public class MyThread implements Runnable{4 @Override5public void run(){6 System.out.println("创建多线程⽅法⼆");7 }8public static void main(String[] args) {9 MyThread thread=new MyThread();10 Thread t=new Thread(thread);11 t.start();12 System.out.println("运⾏结束");13 }1415 }运⾏结果与上述第⼀种的运⾏结果没有什么特殊之处因为Thread类也实现了Runnable接⼝,所以Thread中的构造函数就可以传⼊⼀个Runnable接⼝的对象,也可以传⼊⼀个Thread类的对象 3.实现Callable接⼝实现Callable接⼝,重写call()⽅法例如:package com.dragon.test;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class MyThread implements Callable<string>{public static void main(String[] args) {ExecutorService threadPool=Executors.newSingleThreadExecutor();//启动多线程Future<string> future=threadPool.submit(new MyThread());try{System.out.println("waiting thread to finish");System.out.println(future.get());}catch(Exception e){e.printStackTrace();}}@Overridepublic String call() throws Exception {// TODO Auto-generated method stubreturn "创建多线程⽅法三";}}</string></string> 运⾏结果:Callable接⼝是属于Executor,对⽐与Runnable接⼝功能的区别是:(1).Callable可以在任务结束后提供⼀个返回值,Runnable没有这个功能(2).Callable中的call()⽅法可以抛出异常,⽽Runnable的run()⽅法不能抛出异常(3).运⾏Callable可以拿到⼀个Future对象,Future独享表⽰异步计算的结果,它提供了检查计算是否完成的⽅法。
java 多线程用法
Java多线程用法什么是多线程在计算机科学中,线程(Thread)是指程序执行的最小单元。
一个进程可以包含多个线程,每个线程可以并行地执行不同的任务。
多线程的概念出现是为了提高程序的并发性和响应性。
在Java中,可以使用多种方式实现多线程,如继承Thread类、实现Runnable接口、使用Executor框架等。
本文将介绍Java中常用的多线程用法。
继承Thread类Java中通过继承Thread类来创建线程。
下面是一个简单的例子:public class MyThread extends Thread {public void run() {// 线程执行的代码}public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}}在上面的例子中,我们创建了一个名为MyThread的类,继承自Thread类,并重写了run方法。
run方法定义了线程要执行的代码逻辑。
在main方法中,我们创建了一个MyThread对象,并调用其start方法来启动线程。
实现Runnable接口除了继承Thread类外,还可以通过实现Runnable接口来创建线程。
下面是一个示例:public class MyRunnable implements Runnable {public void run() {// 线程执行的代码}public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();}}在上面的例子中,我们定义了一个名为MyRunnable的类,实现了Runnable接口,并重写了run方法。
在main方法中,我们创建了一个Thread对象,并将MyRunnable对象作为参数传递给Thread的构造函数来创建线程。
JAVA常用设计模式详解大全
JAVA常用设计模式详解大全设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
它是将设计经验系统化的产物,目的是提高代码的可复用性、可维护性和可扩展性。
常用的设计模式主要分为三类:创建型模式、结构型模式和行为型模式。
下面将详细介绍每一种模式及其使用方式。
一、创建型模式1. 单例模式(Singleton Pattern)单例模式用于确保一个类只有一个实例,并提供全局访问方法。
常用于线程池、缓存和日志等场景。
2. 工厂模式(Factory Pattern)工厂模式用于根据不同的输入参数创建不同的实例。
常用于对象的创建过程复杂或者需要隐藏创建逻辑的场景。
3. 抽象工厂模式(Abstract Factory Pattern)抽象工厂模式用于创建一系列相关或依赖的对象,且客户端无需关心具体的实现类。
常用于创建多个产品族的场景。
4. 建造者模式(Builder Pattern)建造者模式用于将一个复杂对象的创建过程和其表示分离,以使同样的创建过程可以创建不同的表示。
常用于构建参数较多的对象。
5. 原型模式(Prototype Pattern)原型模式用于创建对象的克隆,避免了通过new关键字创建对象的性能开销。
常用于创建对象的过程耗费资源较多的场景。
二、结构型模式1. 适配器模式(Adapter Pattern)适配器模式用于将一个类的接口转换为客户端所期望的接口。
常用于不兼容接口之间的适配。
2. 装饰器模式(Decorator Pattern)装饰器模式用于动态地给一个对象添加额外的功能。
常用于对原有类的功能进行扩展或包装。
3. 代理模式(Proxy Pattern)代理模式用于控制对其他对象的访问。
常用于远程代理、虚拟代理、保护代理等场景。
4. 外观模式(Facade Pattern)外观模式用于提供一个简化的接口,隐藏一系列复杂的子系统。
常用于简化复杂系统的接口调用过程。
Java多线程编程详解
Java多线程编程详解线程的同步由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。
Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明synchronized 方法。
如:Java代码1.public synchronized void accessVal(int newVal);synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。
java中的多线程的实现生产者消费者模式
java中的多线程的实现⽣产者消费者模式丈夫类:往银⾏账户⾥存钱,存款[0~10000)的随机数,2秒存⼀次妻⼦类:从银⾏账户⾥取钱,取款[0~10000)的随机数,2秒取⼀次,如果余额不⾜,等到丈夫存了钱,再取public class TestAccount { public static void main(String[] args) { Account account = new Account(); account.setAccount("116854398"); account.setBalance(10); Thread t1 = new Wife(account, TestAccount.class); Thread t2 = new Husband(account, TestAccount.class); t1.start(); t2.start(); }}import java.util.Random;public class Wife extends Thread{ private Account account; private Object lock; public Wife(Account account, Object lock) { this.account = account; this.lock = lock; } public void run() { while (true) { synchronized (lock) { Random random = new Random(); int withDraw = random.nextInt(10000); if (account.getBalance() >= withDraw) { account.setBalance(account.getBalance() - withDraw); System.out.println("妻⼦取了:" + withDraw + "元"); System.out.println(account); try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }}import java.util.Random;public class Husband extends Thread{ private Account account; private Object lock; public Husband(Account account, Object lock) { this.account = account; this.lock = lock; } public void run() { while(true) { synchronized (lock) { Random random = new Random(); int exists = random.nextInt(10000); account.setBalance(account.getBalance() + exists); System.out.println("丈夫存了:" + exists + "元"); System.out.println(account); try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } lock.notify(); } } }}。
Java中常用的设计模式23种JAVA设计模式项目实战教程java数据结构算法
Java中常⽤的设计模式23种JAVA设计模式项⽬实战教程java数据结构算法Java中常⽤的设计模式 23种JAVA设计模式项⽬实战教程java数据结构算法58套Java⾼级架构师视频教程,微服务,⾼并发,分布式,⾼可⽤,⾼性能,集群架构,设计模式,数据结构,中间件,并发编程,虚拟机,⾼可扩展,服务器,数据库,性能调优,负载均衡,安全架构,全⽂检索,权限管理Spring Boot,Spring Cloud⼤型分布式综合电商项⽬实战等视频教程JAVA⾼级架构师技术包含:JAVA架构设计,系统架构,缓存架构,分布式架构,安全架构,微服务,⾼并发,⾼可⽤,⾼可扩展,⾼性能,集群搭建,设计模式,数据结构,中间件,并发编程,JVM虚拟机,性能调优,负载均衡,单点登录,⽇志分析,全⽂检索,任务调度,权限管理,⼯作流,⽹络编程,脚本编程,分布式事务,分库分表,团队协作,持续集成,⾃动化部署,服务器,数据库,图形数据库,项⽬实战,SSM框架,SpringBoot,SpringCloud,Maven,Mybatis,Docker,K8S,Devops,Jenkins,Elasticsearch,Nginx,Tomcat,RabbitMQ,RocketMQ,ActiveMQ,Kafka,Dubbo,Solr,SSO,CAS,OA,Ehcache,Memcached,Activiti,Quartz,Shiro ,Git,Netty ,NIO,Linux,Shell,IDEA,Spring,Springmvc,SpringSecurity,SpringData,VueJS,RectJS,AngularJS,NodeJS,Hadoop,Hbase,Spark,HttpClient,Json,Nosql,Mysql,Redis,MongoDB,Zookeeper,Mycat,Oracle,健康项⽬实战,秒杀系统实战,电商项⽬实战,在线教育实战,P2P⾦融项⽬实战,⼤型分布式综合电商项⽬实战等视频教程......58套精品教程介绍:1、58套精品是掌柜最近整理出的最新教程,都是当下最⽕的技术,最⽕的课程,也是全⽹教程的精品;2、58套资源包含:全套完整⾼清视频、完整源码、配套⽂档;3、知识也是需要投资的,有投⼊才会有产出(保证投⼊产出⽐是⼏百上千倍),如果有⼼的朋友会发现,⾝边投资知识的⼤都是技术经理或者项⽬经理,⼯资⼀般相对于不投资的也要⾼出很多;总⽬录:58套JAVA⾼级架构师,微服务架构,亿级⾼并发,分布式架构,源码剖析系列,项⽬实战,设计模式实战,数据结构与算法,消息中间件,并发编程多线程,服务器系列,数据库,分布式事务,⼤型分布式综合电商项⽬实战视频教程第⼀套:01.【⾼并发课】亿级⾼并发⼤型电商详情页系统的⾼性能与⾼可⽤缓存架构实战视频教程第⼆套:02.【微服务课】微服务架构实战160讲.8⼤核⼼模块精讲.打通架构师进阶之路视频教程第三套:03.【项⽬实战】微服务电商系统从设计到实现全流程讲解基于SpringCloud视频教程第四套:04.【项⽬实战】微服务架构⼴告设计系统实战基于SpringCloud+Kafka+Mysql视频教程第五套:【项⽬实战】精讲SpringBoot2.0互联⽹⾦融理财项⽬实战,开发实战与原理分析视频教程(3套)第01套【主流框架】SpringBoot2.0全新系列精通到实战史上最全的完整版视频教程第02套【主流框架】Spring Boot实战与原理分析视频课程第03套【主流框架】SpringBoot2.0互联⽹⾦融理财系统综合项⽬实战视频课程第六套:06.【微服务课】精通SpringBoot Cloud微服务框架,实战案例与源码剖析视频教程(2套)第01套.Spring Cloud微服务最新技术⼊门到精通视频教程第02套.精通Spring Boot Cloud使⽤并理解框架的原理与底层运作机制视频教程第七套:07.【源码解析】深度剖析Spring Spring5 Mybatis Tomcat源码系列底层框架解析视频教程第⼋套:08.【项⽬实战】微服务容器化综合实践Docker+Kubernetes践⾏DevOps理念 k8s部署落地(3套)第01套:Docker+Kubernetes(k8s)微服务容器化及多技术综合实践视频教程第02套:深⼊系统学习Docker容器技术,实践DevOps理念视频教程第03套:Kubernetes(k8s)落地全程实践企业级应⽤实践从部署到核⼼应⽤视频教程第九套:09.【项⽬实战】从⽆到有搭建中⼩型互联⽹公司后台服务架构与运维架构视频课程第⼗套:10.【设计模式】精讲Java23种设计模式源码分析+内存分析+编程思想+Debug⽅式视频教程第⼗⼀套:11.【项⽬实战】设计模式综合项⽬(实战)设计模式综合应⽤的实战案例视频教程第⼗⼆套:12.【项⽬实战】软件系统功能设计(实战)训练(6个设计案例)视频教程第⼗三套:13.【数据结构】恋上数据结构与算法,程序员修炼编程内功(数组,栈,队列,链表,递归,排序,堆等)第⼗四套:14.【⾼级进阶】深度解析Spring5新特性,Java8~11新特性原理与实践,⾼级进阶实战视频教程第01套:Java8新特性原理,⾼级进阶实战视频教程第02套:Java9、10、11新特性全套精讲视频教程第03套:深⼊浅出spring原理与实践视频课程第04套:Spring5新特性及应⽤举例精讲剖析视频教程第⼗五套:15.【项⽬实战】快速上⼿SSO单点登录开发与项⽬实战单点登录在集群开发的作⽤视频教程(2套)第01套【单点登录】SSO单点登录快速上⼿与项⽬实战视频教程第02套【单点登录】SSO单点登录开发与实战,单点登录在集群开发的作⽤视频教程第⼗六套:16.【⾼级架构】Java架构之消息中间件Kafka RabbitMQ RocketMQ ActiveMq精通实战(4套)01.【中间件】ActiveMq中间件基础到精通⾼级实战视频课程02.【中间件】JAVA-ACE架构师系列课程 Rocketmq03.【中间件】RabbitMQ中间件基础到精通,消息订阅视频课程04.【中间件】Kafka分布式消息中间节原理剖析及实战演练视频课程第⼗七套:17.【项⽬实战】企业⽇志平台⽣产案例实战,⽇志分析之ELK stack实战视频教程第⼗⼋套:18.【⾼级进阶】顶尖⾼⼿系列Elasticsearch快速上⼿篇+⾼⼿进阶篇视频课程第⼗九套:19.【项⽬实战】基于Activiti6.X⼯作流进阶与项⽬实战,Activiti整合Drools视频课程第⼆⼗套:20.【任务调度】Spring+Quartz的分布式任务调度及源码解析视频课程第⼆⼗⼀套:21.【系统学习】Java架构之Shiro权限管理权限设计实现项⽬案例,与Springboot整合教程(3套)第01套.SpringBoot与Shiro整合-权限管理实战视频第02套.Shiro基础到精通,原理与架构视频课程第03套.Apache Shiro权限框架实战+项⽬案例+权限设计实现视频课程第⼆⼗⼆套:22.【系统学习】深⼊学习Zookeeper分布式系统开发实战视频课程第⼆⼗三套:23.【分布式】Dubbo第三⽅⽀付项⽬的系统架构实战视频教程第⼆⼗四套:24.【微服务】基于⽀付系统场景的微服务架构的分布式事务解决⽅案视频课程第⼆⼗五套:25.【项⽬实战】实战技能Linux100讲全⽅位实战讲解视频教程第⼆⼗六套:26.【linux精讲】Shell脚本编程⼤量企业级实例带你全⾯掌握六⼤技术点视频教程第⼆⼗七套:27.【⾼级进阶】⾼并发多线程实训营-Java多线程编程三个阶进阶实战视频教程第⼆⼗⼋套:28.【⾼级架构】架构之⾼并发系统架构实战⽅案 Java⾼并发解决⽅案与并发编程教程第⼆⼗九套:29.【⾼级进阶】深⼊Java并发编程原理与实战线程安全+锁原理+同步容器+实战讲解视频教程第三⼗套:30.【分布式】分布式事务框架Myth+Raincat+Tcc源码解析视频教程第三⼗⼀套:31.【分布式】分布式常见问题解决⽅案,分布式事务与锁,缓存实战解决⽅案视频教程第三⼗⼆套:32.【分布式】解决分布式事务数据⼀致性开发与实践分布式事务实现视频教程第三⼗三套:33.【分布式】分布式集群部署实战,分布式存储缓存协调调度视频教程第三⼗四套:34.【性能优化】深⼊JAVA虚拟机,JVM内核-原理,诊断与优化+内存模型+虚拟机原理视频教程第三⼗五套:35.【性能优化】架构⼤⽜带你学习MySql,Nginx,Tomcat,JVM性能调优系列专题视频教程第三⼗六套:36.【性能优化】深⼊JAVA程序性能调优视频(阿姆达尔定律、缓存组件、并⾏开发、线程池、JVM调优)第三⼗七套:37.【⾼级进阶】全⾯深⼊Mysql数据库系统优化+查询优化,Mysql⼤型分布式集群,从⼩⽩到⼤神(3套)第01套:全⾯深⼊Mysql数据库优化查询优化mysql⾼级第02套【数据库】MySQL⾼级⼤型分布式集群,主从复制,负载均衡,数据库中间件视频课程第03套:Mysql从⼩⽩到⼤神视频教程第三⼗⼋套:38.【⾼级进阶】深⼊进阶Oracle DBA性能优化+⾼可⽤+海量数据库设计视频课程(2套)第三⼗九套:39.【项⽬实战】企业级开发与运维Redis从⼊门到项⽬实战视频教程第四⼗套:40.【项⽬实战】精通MongoDB4.0从⼊门到实践,掌握NoSQL数据库企业主流解决⽅案视频教程第四⼗⼀套:41.【⾼级架构】Java架构之Mycat实现mysql⾼可⽤集群,分布库分表中间件视频教程第四⼗⼆套:42.【数据库】图形数据库之王 Neo4j从⼊门到精通视频教程第四⼗三套:43.【⾼级进阶】企业级Nginx核⼼知识,百万并发下的Nginx性能优化之道视频教程。
java中的多线程
java中的多线程在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable 接口。
对于直接继承Thread的类来说,代码大致框架是:先看一个简单的例子:【运行结果】:A运行0 A运行1 A运行2 A运行3 A运行4 B运行0 B运行1 B运行 2 B运行3 B运行4我们会发现这些都是顺序执行的,说明我们的调用方法不对,应该调用的是start()方法。
当我们把上面的主函数修改为如下所示的时候:然后运行程序,输出的可能的结果如下:A运行 0 B运行 0 B运行 1 B运行 2 B运行 3 B运行 4 A运行 1 A运行 2 A运行 3 A运行 4因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。
注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体。
那么:为什么我们不能直接调用run()方法呢?我的理解是:线程的运行需要本地操作系统的支持。
如果你查看start的源代码的时候,会发现:注意我用红色加粗的那一条语句,说明此处调用的是start0()。
并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。
因为多线程的实现需要本地操作系统的支持。
但是start方法重复调用的话,会出现ng.IllegalThreadStateException异常。
通过实现Runnable接口:大致框架是:来先看一个小例子吧:【可能的运行结果】:线程A运行 0 线程B运行 0 线程B运行 1 线程B运行 2线程B运行 3 线程B运行 4 线程A运行 1线程A运行 2 线程A运行 3 线程A运行 4关于选择继承Thread还是实现Runnable接口?其实Thread也是实现Runnable接口的:其实Thread中的run方法调用的是Runnable接口的run方法。
不知道大家发现没有,T hread和Runnable都实现了run方法,这种操作模式其实就是代理模式。
Java中的设计模式和架构模式详解
Java中的设计模式和架构模式详解设计模式和架构模式是软件开发中非常重要的概念,它们可以帮助开发人员更好地设计和构建高质量的软件系统。
在Java中,设计模式和架构模式被广泛应用,许多经典的设计模式和架构模式都有对应的Java实现。
一、设计模式设计模式是针对软件设计中常见问题的解决方案的模板。
它们提供了一种通用的设计方法,可以帮助开发人员更好地组织和管理代码。
在Java中,最常用的设计模式包括:1.工厂模式(Factory Pattern):工厂模式是一种创建型设计模式,用于创建对象而不暴露创建逻辑。
它可以帮助我们将对象的创建和使用解耦,使系统更加灵活和可维护。
2.单例模式(Singleton Pattern):单例模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。
在Java中,单例模式通常通过私有化构造函数、静态变量和静态方法实现。
3.观察者模式(Observer Pattern):观察者模式是一种行为设计模式,用于实现对象之间的一对多依赖关系。
在Java中,观察者模式通常通过Java内置的Observer接口和Observable类实现。
4.策略模式(Strategy Pattern):策略模式是一种行为设计模式,用于定义一系列算法,并将每个算法封装起来,使它们可以互相替换。
在Java中,策略模式常常通过接口和实现类实现。
5.适配器模式(Adapter Pattern):适配器模式是一种结构设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。
在Java中,适配器模式通常通过接口实现或类继承实现。
以上只是部分常见的设计模式,在Java中还有许多其他设计模式,每种设计模式都有其特定的使用场景和优缺点,开发人员可以根据具体情况选择合适的设计模式来解决问题。
二、架构模式架构模式是指软件系统整体结构的模式,它可以指导系统的整体设计和组织。
在Java中,常见的架构模式包括:1.模型-视图-控制器模式(Model-View-Controller,MVC):MVC 是一种使用最广泛的架构模式,它将应用程序分为模型(Model)、视图(View)和控制器(Controller)三个部分,分别负责数据处理、用户界面和业务逻辑。
Java编程中的多线程编程
Java编程中的多线程编程随着科技的飞速发展,软件开发已然成为一门热门职业,而Java作为其中的佼佼者,更是备受重视。
而Java中的多线程编程便是其中一项难点。
一、为何需要多线程编程在计算机领域中,多线程技术是用于使一个应用程序同时有多个执行路径的技术。
而在Java编程中,多线程技术是为了充分利用多核CPU处理能力,加快程序运行速度。
在Java编程中,CPU中有多个处理器,如果只使用单线程程序,则只能充分利用其中一个处理器的处理能力,导致其他处理器的闲置浪费。
因此,使用多线程技术可以使Java程序同时在多个核中执行,充分利用CPU的处理能力,从而达到提升程序运行速度的效果。
二、多线程编程的实现方式在Java中,实现多线程编程有两种方式:一种通过继承Thread 类,另一种则是通过实现Runnable接口。
1. 继承Thread类继承Thread类即是将自己定义的类继承Thread,重写run()方法,在run()方法中编写代码逻辑。
例如:```javapublic class MyThread extends Thread {@Overridepublic void run() {// 这里可以编写线程的业务逻辑}}```在主程序中,通过创建MyThread类的对象,调用start()方法启动线程,例如:```javapublic class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}}```2. 实现Runnable接口实现Runnable接口是将自己定义的类实现Runnable接口,重写run()方法,在run()方法中编写代码逻辑。
例如:```javapublic class MyRunnable implements Runnable {@Overridepublic void run() {// 这里可以编写线程的业务逻辑}}```在主程序中,通过创建Thread类的对象,并将MyRunnable的对象作为参数传入Thread类的构造方法中,调用start()方法启动线程,例如:```javapublic class Main {public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();}}```三、多线程编程中的同步与锁在多线程编程中,为了保证线程之间数据的同步性,需要使用同步与锁技术。
23种设计模式及案例整理分享
23种设计模式及案例整理分享创建型模式⼯⼚模式⼯⼚模式(Factory Pattern)是 Java 中最常⽤的设计模式之⼀。
这种类型的设计模式属于创建型模式,它提供了⼀种创建对象的最佳⽅式。
在⼯⼚模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使⽤⼀个共同的接⼝来指向新创建的对象。
介绍意图:定义⼀个创建对象的接⼝,让其⼦类⾃⼰决定实例化哪⼀个⼯⼚类,⼯⼚模式使其创建过程延迟到⼦类进⾏。
主要解决:主要解决接⼝选择的问题。
何时使⽤:我们明确地计划不同条件下创建不同实例时。
如何解决:让其⼦类实现⼯⼚接⼝,返回的也是⼀个抽象的产品。
关键代码:创建过程在其⼦类执⾏。
应⽤实例: 1、您需要⼀辆汽车,可以直接从⼯⼚⾥⾯提货,⽽不⽤去管这辆汽车是怎么做出来的,以及这个汽车⾥⾯的具体实现。
2、Hibernate 换数据库只需换⽅⾔和驱动就可以。
优点: 1、⼀个调⽤者想创建⼀个对象,只要知道其名称就可以了。
2、扩展性⾼,如果想增加⼀个产品,只要扩展⼀个⼯⼚类就可以。
3、屏蔽产品的具体实现,调⽤者只关⼼产品的接⼝。
缺点:每次增加⼀个产品时,都需要增加⼀个具体类和对象实现⼯⼚,使得系统中类的个数成倍增加,在⼀定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
这并不是什么好事。
使⽤场景: 1、⽇志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,⽤户可以选择记录⽇志到什么地⽅。
2、数据库访问,当⽤户不知道最后系统采⽤哪⼀类数据库,以及数据库可能有变化时。
3、设计⼀个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现⼀个接⼝。
注意事项:作为⼀种创建类模式,在任何需要⽣成复杂对象的地⽅,都可以使⽤⼯⼚⽅法模式。
有⼀点需要注意的地⽅就是复杂对象适合使⽤⼯⼚模式,⽽简单对象,特别是只需要通过 new 就可以完成创建的对象,⽆需使⽤⼯⼚模式。
Java多线程设计模式系列
Java多线程设计模式系列通过⼏天的认真阅读,发现这是⼀本难得⼀见的好书,为了加深巩固学习成功,我打算将书中的例⼦全部⾃⼰实现⼀遍,特此记录下来也⽅便其他朋友学习。
第⼀章,java语⾔的线程单线程程序:打印10000次good字符串public class SingleThreadSample {public static void main(String[] args) {for(int i=0; i< 10000; i++){System.out.print("good!");}}}严格的说并不是只有⼀个线程在操作,还有其他的线程在⾮java处理系统上运⾏,⽐如gc,gui相关的线程等。
第⼀个多线程程序:实现了交替打印good和nice的功能public class MyThreadTest {public static void main(String[] args) {MyThread t = new MyThread();t.start();for (int i = 0; i < 10000; i++) {System.out.println("good!");}}}class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println("nice!");}}}这⾥加⼊⼀个并发和并⾏概念的区别,并发是concurrent,是指多个线程在同⼀个cpu上切换进⾏执⾏。
并⾏是parallel,指多个线程是在各⾃的cpu上同时执⾏的。
我们增强⼀下刚才的多线程例⼦,把打印的字符串变成通过参数传递。
public class MyThread2Test {public static void main(String[] args) {MyThread2 t1 = new MyThread2("good!");MyThread2 t2 = new MyThread2("nice!");t1.start();t2.start();}}class MyThread2 extends Thread {private String message;public MyThread2(String message) {this.message = message;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println(message);刚才是通过集成Thread抽象类的⼦类⽅式实现多线程,另外还可以通过Runnable接⼝的⽅式,例⼦如下:public class MyThread3Test {public static void main(String[] args) {MyThread3 t1 = new MyThread3("good!");MyThread3 t2 = new MyThread3("nice!");new Thread(t1).start();new Thread(t2).start();}}class MyThread3 implements Runnable {private String message;public MyThread3(String message) {this.message = message;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println(message);}}}启动和执⾏多线程已经说完了,那么该说说如何让线程休息休息。
如何使用Java编写多线程网络编程
如何使用Java编写多线程网络编程Java是一门开发网络应用的高级编程语言,Java的多线程编程能力也为开发人员提供了丰富的选择。
在网络编程领域,Java多线程可以轻松地处理大量连接和数据流。
在这篇文章中,我们将探讨如何使用Java编写多线程网络编程。
1. 多线程的概念在Java中,多线程是指在同一时间内运行多个线程的技术。
多线程的优势在于可以同时执行多个任务,提高程序的执行效率。
在网络编程中,多线程技术可以帮助程序同时处理多个网络连接。
2. 多线程网络编程的基础在多线程网络编程中,我们通常使用Java的套接字(Socket)类和线程(Thread)类。
2.1 套接字(Socket)类Java的套接字类是用于处理网络连接的类。
通过套接字类,我们可以创建客户端和服务器端的连接,发送和接收数据,关闭连接等操作。
在Java中,套接字类有两种类型:ServerSocket和Socket。
ServerSocket是服务器端套接字类,主要用于监听客户端的连接请求。
而Socket是客户端套接字类,用于发起连接请求。
2.2 线程(Thread)类Java的线程类是用于处理多线程的类。
通过线程类,我们可以创建多个线程,分别执行不同的任务。
在多线程网络编程中,我们通常会创建两个线程,一个用于监听连接请求,一个用于处理连接请求。
3. 编写客户端程序下面是一个基本的客户端程序,用于连接服务器,发送和接收数据。
```javaimport .*;import java.io.*;public class Client {public static void main(String[] args) {try {Socket socket = new Socket("localhost", 8000);System.out.println("Connected to server");// 获取输入输出流DataInputStream in = newDataInputStream(socket.getInputStream());DataOutputStream out = newDataOutputStream(socket.getOutputStream());// 发送数据out.writeUTF("Hello from client");// 接收数据String response = in.readUTF();System.out.println("Response from server: " + response);// 关闭连接socket.close();} catch (IOException e) {e.printStackTrace();}}}```4. 编写服务器端程序下面是一个基本的服务器端程序,用于监听连接请求,接收和发送数据。
详解三种java实现多线程的方式
详解三种java实现多线程的⽅式java中实现多线程的⽅法有两种:继承Thread类和实现runnable接⼝。
1.继承Thread类,重写⽗类run()⽅法public class thread1 extends Thread {public void run() {for (int i = 0; i < 10000; i++) {System.out.println("我是线程"+this.getId());}}public static void main(String[] args) {thread1 th1 = new thread1();thread1 th2 = new thread1();th1.run();th2.run();}}run()⽅法只是普通的⽅法,是顺序执⾏的,即th1.run()执⾏完成后才执⾏th2.run(),这样写只⽤⼀个主线程。
多线程就失去了意义,所以应该⽤start()⽅法来启动线程,start()⽅法会⾃动调⽤run()⽅法。
上述代码改为:public class thread1 extends Thread {public void run() {for (int i = 0; i < 10000; i++) {System.out.println("我是线程"+this.getId());}}public static void main(String[] args) {thread1 th1 = new thread1();thread1 th2 = new thread1();th1.start();th2.start();}}通过start()⽅法启动⼀个新的线程。
这样不管th1.start()调⽤的run()⽅法是否执⾏完,都继续执⾏th2.start()如果下⾯有别的代码也同样不需要等待th2.start()执⾏完成,⽽继续执⾏。
Java多线程设计模式
Java多线程设计模式/it-d225a33ad6e947cea997cc02b1826e7f-1线程的创建和启动Java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就结束了。
一旦一个线程执行完毕,这个实例就不能再重新启动,只能重新生成一个新实例,再启动一个新线程。
Thread类是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法:view sourceprint?1.Thread t = new Thread();2.t.start();start()方法是一个native方法,它将启动一个新线程,并执行run()方法。
Thread类默认的run()方法什么也不做就退出了。
注意:直接调用run()方法并不会启动一个新线程,它和调用一个普通的java方法没有什么区别。
因此,有两个方法可以实现自己的线程:方法1:自己的类extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
例如:view sourceprint?1.public class MyThread extends Thread {2. public run() {3. System.out.println("MyThread.run()");4. }5.}在合适的地方启动线程:new MyThread().start();方法2:如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口:view sourceprint?1.public class MyThread extends OtherClass implements Runnable {2. public run() {3. System.out.println("MyThread.run()");4. }5.}为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:view sourceprint?1.MyThread myt = new MyThread();2.Thread t = new Thread(myt);3.t.start();事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:view sourceprint?1.public void run() {2. if (target != null) {3. target.run();4. }5.}线程还有一些Name, ThreadGroup, isDaemon等设置,由于和线程设计模式关联很少,这里就不多说了。
Java多线程设计模式之线程池模式.
Java多线程设计模式之线程池模式前序:Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作。
它将“委托消息的一端”和“执行消息的一端”用两个不同的线程来实现。
该线程模式主要包括三个部分:1,Request参与者(委托人,也就是消息发送端或者命令请求端2,Host参与者,接受消息的请求,负责为每个消息分配一个工作线程。
3,Worker参与者,具体执行Request参与者的任务的线程,由Host参与者来启动。
由于常规调用一个方法后,必须等待该方法完全执行完毕后才能继续执行下一步操作,而利用线程后,就不必等待具体任务执行完毕,就可以马上返回继续执行下一步操作。
背景:由于在Thread-Per-Message Pattern中对于每一个请求都会生成启动一个线程,而线程的启动是很花费时间的工作,所以鉴于此,提出了Worker Thread,重复利用已经启动的线程。
线程池:Worker Thread,也称为工人线程或背景线程,不过一般都称为线程池。
该模式主要在于,事先启动一定数目的工作线程。
当没有请求工作的时候,所有的工人线程都会等待新的请求过来,一旦有工作到达,就马上从线程池中唤醒某个线程来执行任务,执行完毕后继续在线程池中等待任务池的工作请求的到达。
任务池:主要是存储接受请求的集合,利用它可以缓冲接受到的请求,可以设置大小来表示同时能够接受最大请求数目。
这个任务池主要是供线程池来访问。
线程池:这个是工作线程所在的集合,可以通过设置它的大小来提供并发处理的工作量。
对于线程池的大小,可以事先生成一定数目的线程,根据实际情况来动态增加或者减少线程数目。
线程池的大小不是越大越好,线程的切换也会耗时的。
存放池的数据结构,可以用数组也可以利用集合,在集合类中一般使用Vector,这个是线程安全的。
Worker Thread的所有参与者:1,Client参与者,发送Request的参与者2,Channel 参与者,负责缓存Request 的请求,初始化启动线程,分配工作线程 3,Worker 参与者,具体执行Request 的工作线程 4,Request 参与者注意:将在Worker 线程内部等待任务池非空的方式称为正向等待。
Java多线程之WorkerThread模式
Java多线程之WorkerThread模式⽬录⼀.Worker Thread模式⼆ .Worker Thread模式中的⾓⾊1.Client(委托者)2.Channel(通信线路)3.Worker(⼯⼈)4.Request(请求)三.Worker Thread使⽤场景四.Worker Thread模式程序⽰例⼀.Worker Thread模式Worker的意思是⼯作的⼈,在Worker Thread模式中,⼯⼈线程Worker thread会逐个取回⼯作并进⾏处理,当所有⼯作全部完成后,⼯⼈线程会等待新的⼯作到来。
Worker Thread模式也被成为Background Thread(背景线程)模式,另外,如果从保存多个⼯⼈线程的场所这⼀点看,我们也可以称这种模式为Thread Pool模式。
⼆ .Worker Thread模式中的⾓⾊1.Client(委托者)创建表⽰⼯作请求的Request并将其传递给Channel。
在⽰例程序中,ClientThread相当于该⾓⾊。
2.Channel(通信线路)Channel⾓⾊接受来⾃于Client的Request,并将其传递给Worker。
在⽰例程序中,Channel相当于该⾓⾊。
3.Worker(⼯⼈)Worker⾓⾊从Channel中获取Request,并进⾏⼯作,当⼀项⼯作完成后,它会继续去获取另外的Request,在⽰例程序中,WorkerThread相当于该⾓⾊。
4.Request(请求)Request⾓⾊是表⽰⼯作的⾓⾊,Request⾓⾊中保存了进⾏⼯作所必须的信息,在⽰例程序中,Request相当于该⾓⾊。
三.Worker Thread使⽤场景想象⼀个场景,⼀个⼯⼚在⽣产玩具,在⼀个车间⾥,有⼏个⼯⼈,每次⽣产部件准备好车间外的⼈就将部件放到车间的⼀个桌⼦上,⼯⼈每次做完⼀个玩具就从桌⼦上取部件。
在这⾥,注意到,部件并不是直接交给⼯⼈的,另外⼀点,⼯⼈并不是做完⼀个部件就回家换个新⼈,后者在现实有点滑稽,但是在程序中却对应⼀个典型的线程使⽤⽅法:线程池。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
多线程经典实例java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就结束了。
一旦一个线程执行完毕,这个实例就不能再重新启动,只能重新生成一个新实例,再启动一个新线程。
Thread类是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法:Thread t = new Thread();t.start();start()方法是一个native方法,它将启动一个新线程,并执行run()方法。
Thread类默认的run()方法什么也不做就退出了。
注意:直接调用run()方法并不会启动一个新线程,它和调用一个普通的java方法没有什么区别。
因此,有两个方法可以实现自己的线程:方法1:自己的类extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
例如:public class MyThread extends Thread {public run() {System.out.println("MyThread.run()");}}在合适的地方启动线程:new MyThread().start();方法2:如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口:public class MyThread extends OtherClass implements Runnable {public run() {System.out.println("MyThread.run()");}}为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:MyThread myt = new MyThread();Thread t = new Thread(myt);t.start();事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:public void run() {if (target != null) {target.run();}}线程还有一些Name, ThreadGroup, isDaemon等设置,由于和线程设计模式关联很少,这里就不多说了。
由于同一进程内的多个线程共享内存空间,在Java中,就是共享实例,当多个线程试图同时修改某个实例的内容时,就会造成冲突,因此,线程必须实现共享互斥,使多线程同步。
最简单的同步是将一个方法标记为synchronized,对同一个实例来说,任一时刻只能有一个synchronized方法在执行。
当一个方法正在执行某个synchronized方法时,其他线程如果想要执行这个实例的任意一个synchronized方法,都必须等待当前执行 synchronized方法的线程退出此方法后,才能依次执行。
但是,非synchronized方法不受影响,不管当前有没有执行synchronized方法,非synchronized 方法都可以被多个线程同时执行。
此外,必须注意,只有同一实例的synchronized方法同一时间只能被一个线程执行,不同实例的synchronized方法是可以并发的。
例如,class A定义了synchronized方法sync(),则不同实例a1.sync()和a2.sync()可以同时由两个线程来执行。
多线程同步的实现最终依赖锁机制。
我们可以想象某一共享资源是一间屋子,每个人都是一个线程。
当A希望进入房间时,他必须获得门锁,一旦A获得门锁,他进去后就立刻将门锁上,于是B,C,D...就不得不在门外等待,直到A释放锁出来后,B,C,D...中的某一人抢到了该锁(具体抢法依赖于 JVM的实现,可以先到先得,也可以随机挑选),然后进屋又将门锁上。
这样,任一时刻最多有一人在屋内(使用共享资源)。
Java语言规范内置了对多线程的支持。
对于Java程序来说,每一个对象实例都有一把“锁”,一旦某个线程获得了该锁,别的线程如果希望获得该锁,只能等待这个线程释放锁之后。
获得锁的方法只有一个,就是synchronized关键字。
例如:public class SharedResource {private int count = 0;public int getCount() { return count; }public synchronized void setCount(int count) { this.count = count; }}同步方法public synchronized void setCount(int count) { this.count = count; } 事实上相当于:public void setCount(int count) {synchronized(this) { // 在此获得this锁this.count = count;} // 在此释放this锁}红色部分表示需要同步的代码段,该区域为“危险区域”,如果两个以上的线程同时执行,会引发冲突,因此,要更改SharedResource的内部状态,必须先获得SharedResource实例的锁。
退出synchronized块时,线程拥有的锁自动释放,于是,别的线程又可以获取该锁了。
为了提高性能,不一定要锁定this,例如,SharedResource有两个独立变化的变量:public class SharedResouce {private int a = 0;private int b = 0;public synchronized void setA(int a) { this.a = a; }public synchronized void setB(int b) { this.b = b; }}若同步整个方法,则setA()的时候无法setB(),setB()时无法setA()。
为了提高性能,可以使用不同对象的锁:public class SharedResouce {private int a = 0;private int b = 0;private Object sync_a = new Object();private Object sync_b = new Object();public void setA(int a) {synchronized(sync_a) {this.a = a;}}public synchronized void setB(int b) {synchronized(sync_b) {this.b = b;}}}通常,多线程之间需要协调工作。
例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程 downloadThread将该图片下载完毕。
如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。
以上逻辑简单的说就是:如果条件不满足,则等待。
当条件满足时,等待该条件的线程将被唤醒。
在Java中,这个机制的实现依赖于wait/notify。
等待机制与锁机制是密切关联的。
例如:synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();}当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:synchronized(obj) {condition = true;obj.notify();}需要注意的概念是:# 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {...} 代码段内。
# 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {...} 代码段内唤醒A。
# 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
# 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
# obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。
直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
前面讲了wait/notify机制,Thread还有一个sleep()静态方法,它也能使线程暂停一段时间。
sleep与wait的不同点是: sleep并不释放锁,并且sleep的暂停和wait暂停是不一样的。
obj.wait会使线程进入obj对象的等待集合中并等待唤醒。
但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。
如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。
对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。
但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException。