第1关:单例模式
任务描述
在企业网站后台系统中,一般会将网站统计单元进行独立设计,比如登录人数的统计、IP 数量的计数等。在这类需要完成全局统计的过程中,就会用到单例模式,即整个系统只需要拥有一个计数的全局对象。
本关任务:模拟网站登录,高并发场景。模拟 10 个登录线程,程序输出登录总数。
实现要点
在类中添加一个私有静态成员变量用于保存单例实例。
声明一个公有静态构建方法用于获取单例实例。
在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。
将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。
检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。
编程要求
本任务有三个文件"Login.java"、"Client.java"和"Singleton.java",在右侧编辑器 Begin-End 内补充 Singleton 中代码,其它文件请阅读代码。
话不多说,直接上代码
Singleton.java
java
package step1;
import java.util.concurrent.atomic.AtomicLong;
public class Singleton {
private static Singleton instance;
//AtomicLong是以原子方式操作long值的类,作用是保证并发时线程安全的累加
private AtomicLong count = new AtomicLong(0);
/********** Begin *********/
//此处增加Singleton的构造函数
private Singleton() {
}
/********** End *********/
public static Singleton GetInstance(){
/********** Begin *********/
//考虑线程安全问题
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
/********** End *********/
public AtomicLong getCount() {
return count;
}
public void setCount() {
count.addAndGet(1);
}
}
Login.java
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
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()+"个用户登录");
}
}