单例模式介绍:
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供全局访问点来访问该实例。
在Java中,创建单例模式的常用方法是使用一个私有的构造方法和一个静态方法来获取实例。在静态方法中,通过判断实例是否已经存在,如果已经存在就返回该实例,如果不存在就创建一个新的实例并返回。
任务描述
在企业网站后台系统中,一般会将网站统计单元进行独立设计,比如登录人数的统计、IP 数量的计数等。在这类需要完成全局统计的过程中,就会用到单例模式,即整个系统只需要拥有一个计数的全局对象。
本关任务:模拟网站登录,高并发场景。模拟 10 个登录线程,程序输出登录总数。
实现要点
- 在类中添加一个私有静态成员变量用于保存单例实例。
- 声明一个公有静态构建方法用于获取单例实例。
- 在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。
- 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。
- 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。
编程要求
本任务有三个文件“Login.java”、“Client.java”和“Singleton.java”,在右侧编辑器 Begin-End 内补充 Singleton 中代码,其它文件请阅读代码。
代码:Singleton.java
package step1;
import java.util.concurrent.atomic.AtomicLong;
public class Singleton {
private static Singleton instance;
private AtomicLong count = new AtomicLong(0);
private Singleton() {
}
public static Singleton GetInstance(){
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public AtomicLong getCount() {
return count;
}
public void setCount() {
count.addAndGet(1);
}
}
代码:Login.java
package step1;
//Runnable 接口由其实现类来由线程执行对应的实例。对于实现类必须是实现方法 run
public class Login implements Runnable{
private String loginname;
public String getLoginname() {
return loginname;
}
public void setLoginname(String loginname) {
this.loginname = loginname;
}
@Override
public void run() {
Singleton lazySingleton =Singleton.GetInstance();
lazySingleton.setCount();
/*调试时观察
System.out.println(getLoginname()+"登录成功."+lazySingleton);
*/
}
}
代码:Client.java
package step1;
public class Client {
public final static int num = 10;
public static void main(String[] args) throws InterruptedException {
///创建10个线程,模拟10个用户登录
Thread[] threads = new Thread[num];
for (int i = 0; i < num; i++) {
Login login = new Login();
login.setLoginname("" + String.format("%2s", (i + 1)) + "号用户");
//创建了线程threads[i],并把login对象(已实现Runnable接口)放入线程中
threads[i]=new Thread(login);
//线程状态转换为RUNNABLE
threads[i].start();
}
for (Thread thread : threads) {
//Client等待线程结束之后才能继续运行,防止最后一行的System.out.println提前运行
thread.join();
}
System.out.println("网站共有"+Singleton.GetInstance().getCount()+"个用户登录");
}
}