写在前面
因为让类只能实例化指定个数的对象需要有单例模式的基础,所以博主对单例模式进行了解释,如果想看本题代码实现的可以直接跳到最后
题目设计
-
写一个类LimitInstanceClass,该类最多可以实例化指定个数实例。实例的个数用配置文件InstanceLimit.cfg指定。例如,如果InstanceLimit.cfg的内容为2,则LimitInstanceClass最多可以同时存在2个对象。
-
LimitInstanceClass的对象有一个
整型成员变量id
,保存对象的编号;有一个
boolean型变量isBusy
,如果该变量的值为true,表示该对象正在被使用,否则该对象空闲。getInstance()方法判断是否存在空闲的对象
,存在将该空闲对象的isBusy置为true,并返回该对象;如果不存在空闲对象则返回null。LimitInstanceClass有一个release()方法
,该方法将对象的isBusy置为false。LimitInstanceClass还有一个String类型的成员变量accessMessage
,以及一个成员方法writeAccessMessage(String message)
,该方法将参数message追加到accessMessage。LimitInstanceClass的printAccessMessage()方法
输出accessMessage的内容。 -
编写一个线程类AccessLimitInstanceClassThread,在其run()方法中获取一个LimitInstanceClass对象,调用获得的对象的writeAccessMessage(String message)将自己的线程名写入accessMessage,随机休眠0-5秒,再调用printAccessMessage(),最后调用release()方法。
-
编写一个UseLimitInstanceClass类,在其main方法中实例化10个AccessLimitInstanceClassThread线程对象,并启动各个线程。设置InstanceLimit.cfg的内容为3,写出你的程序的运行结果。
单例模式
首先来看一下单例模式怎么实现,即怎么让一个类只能实例化一个对象
**意图:**保证一个类仅有一个实例,并提供一个访问它的全局访问点。
**主要解决:**一个全局使用的类频繁地创建与销毁。
**何时使用:**当您想控制实例数目,节省系统资源的时候。
**如何解决:**判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
**关键代码:**构造函数是私有的。
应用实例:
- 1、一个班级只有一个班主任。
- 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
- 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
- 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
**缺点:**没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种方式是最基本的单例模式实现方式,但仍然存在一些问题,如运行效率低等,但在这里不再展开。
步入正题
与单例模式的实现不同,因为要实例化指定个数的对象,所以需要一个对象数组,同时将指定个数的对象实例化后填入对象数组
(否则只是生成了指定长度的对象数组,数组内都是null)
代码实现
InstanceLimit.xml
这是xml文件,用于读取实例化个数。博主没有根据题设从cfg文件中读取而是改从xml文件中读取实例化个数
该XML文件放在和其他Java文件平级的目录下(同一个package下)
<?xml version="1.0" encoding="UTF-8"?>
<project>
<LimitInstance>
<length>3</length>
</LimitInstance>
</project>
ParseXML.java
该类用于读取XML文件的内容
package limitInstance;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author 作者 Long:
* @version 创建时间:2022年4月27日 下午9:58:50
*/
public class ParseXML {
// 用Element方式
public static void element(NodeList list) {
for (int i = 0; i < list.getLength(); i++) {
Element element = (Element) list.item(i);
NodeList childNodes = element.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
if (childNodes.item(j).getNodeType() == Node.ELEMENT_NODE) {
// 获取节点
System.out.print(childNodes.item(j).getNodeName() + ":");
// 获取节点值
System.out.println(childNodes.item(j).getFirstChild().getNodeValue());
}
}
}
}
// 获取长度
public static int getLength() {
NodeList list = null;
// 1.创建DocumentBuilderFactory对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2.创建DocumentBuilder对象
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document d = builder.parse("src/limitInstance/InstanceLimit.xml");
list = d.getElementsByTagName("LimitInstance");
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < list.getLength(); i++) {
Element element = (Element) list.item(i);
NodeList childNodes = element.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
if (childNodes.item(j).getNodeType() == Node.ELEMENT_NODE) {
// 获取节点
if (childNodes.item(j).getNodeName().equals("length")) {
// 获取节点值
return Integer.parseInt(childNodes.item(j).getFirstChild().getNodeValue());
}
}
}
}
return 0;
}
}
LimitInstanceClass.java
这是所有代码的核心,限制实例化个数的类,对象数组的生成
和将对象填充到对象数组
都封装为static静态的,使他们在类封装的时候就提前执行
同时博主增加了函数getUnBusyNum()
来输出空闲的对象数目
package limitInstance;
/**
* @author 作者 Long:
* @version 创建时间:2022年4月27日 下午10:39:05
*/
public class LimitInstanceClass {
private static int num = ParseXML.getLength();
private int id;
private String accessMessage;
private static LimitInstanceClass[] instanceList = new LimitInstanceClass[num];
static {
instanceList = new LimitInstanceClass[num];
for (int i = 0; i < instanceList.length; i++) {
instanceList[i] = new LimitInstanceClass();
instanceList[i].id = i;
}
}
private LimitInstanceClass() {
} // 声明私有构造函数,使得在本类外不能实例化本类对象
public int getId() {
return id;
}
private boolean isBusy = false;
public static synchronized LimitInstanceClass getInstance() {
// if (instanceList == null) {
// instanceList = new LimitInstanceClass[num];
// }
// System.out.println(instanceList.length);
for (int i = 0; i < instanceList.length; i++) {
// System.out.println(instanceList[i]);
if (instanceList[i].isBusy == false) {
instanceList[i].isBusy = true;
return instanceList[i];
}
}
return null;
}
public void release() {
this.isBusy = false;
}
public void writeAccessMessage(String message) {
this.accessMessage = message;
}
public String printAccessMessage() {
return this.accessMessage;
}
public static int getUnBusyNum() {
int num = 0;
for (int i = 0; i < instanceList.length; i++) {
if (instanceList[i].isBusy == false) {
num++;
}
}
return num;
}
}
AccessLimitInstanceClassThread.java
线程类,run方法中是一个死循环,线程会不断申请调用对象
- 申请获取对象
- 将线程自己的写入
- 打印信息(“某某线程调用了某某对象”)
- 打印空闲的对象数目
- 释放对象
- 睡眠一段时间
package limitInstance;
import java.util.Random;
/**
* @author 作者 Long:
* @version 创建时间:2022年4月27日 下午10:30:09
*/
public class AccessLimitInstanceClassThread extends Thread{
private String name;
public AccessLimitInstanceClassThread(String name){
this.name = name;
}
@Override
public void run() {
while (true) {
LimitInstanceClass li = LimitInstanceClass.getInstance();
if (li==null) {
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
li.writeAccessMessage(name);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:"+li.printAccessMessage()+" 调用了对象:"+li.getId());
System.out.println("剩余空闲变量:"+LimitInstanceClass.getUnBusyNum());
li.release();
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Main.java
Main方法生成十个线程,并让他们分别运行,观察结果
package limitInstance;
/**
* @author 作者 Long:
* @version 创建时间:2022年4月27日 下午9:42:30
*/
public class Main {
public static void main(String[] args) {
AccessLimitInstanceClassThread thread1 = new AccessLimitInstanceClassThread("thread--1");
thread1.start();
AccessLimitInstanceClassThread thread2 = new AccessLimitInstanceClassThread("thread--2");
thread2.start();
AccessLimitInstanceClassThread thread3 = new AccessLimitInstanceClassThread("thread--3");
thread3.start();
AccessLimitInstanceClassThread thread4 = new AccessLimitInstanceClassThread("thread--4");
thread4.start();
AccessLimitInstanceClassThread thread5 = new AccessLimitInstanceClassThread("thread--5");
thread5.start();
AccessLimitInstanceClassThread thread6 = new AccessLimitInstanceClassThread("thread--6");
thread6.start();
AccessLimitInstanceClassThread thread7 = new AccessLimitInstanceClassThread("thread--7");
thread7.start();
AccessLimitInstanceClassThread thread8 = new AccessLimitInstanceClassThread("thread--8");
thread8.start();
AccessLimitInstanceClassThread thread9 = new AccessLimitInstanceClassThread("thread--9");
thread9.start();
AccessLimitInstanceClassThread thread10 = new AccessLimitInstanceClassThread("thread--10");
thread10.start();
}
}