Singleton Pattern
In a software system, sometimes you do not need more than one instance of a particular class in the JVM. And in some cases, having more than one instance will in fact negatively impact the performance of the entire software system. In such cases, you adopt the Singleton pattern.
When the Singleton pattern is used for a particular class, you can only create one object of that class. The class is constructed in such a way that you cannot instantiate more than one instance.
Here is the generic template for such a class:
public class MySingleton {
private static MySingleton singleton = new MySingleton();
private MySingleton() {
}
public static MySingleton getInstance() {
return singleton;
}
}
Notice the key points in a Singleton:
- The constructor is made private so that no other class can instantiate it.
- A static method, typically called
getInstance(), is the only way you can get an instance of this class. This method returns the private static variable that is initialized with an instance of this class.
In the example above, the class is loaded with a static variable that is initialized with the instance. However, you could delay creating an instance until the getInstance() method is called. Here is such an implementation:
public class MySingleton {
private static MySingleton singleton;
private MySingleton() {
}
public static MySingleton getInstance() {
if (singleton == null) {
singleton = new MySingleton();
}
return singleton;
}
}
The implementation above is helpful when the application may or may not use MySingleton while running. In that case, instantiation only happens if it is really required.
Singleton Example
One of the classic examples for which you should use a Singleton is in creating a database connection pool object. So far, whenever you wanted a Connection object to start a conversation with a database, you created a Connection object. For small loads, this is perfect. However, if your website is receiving a large number of clients and each one is invoking a connection to the database, then you will exhaust the maximum number of connections that the database can handle.
In such situations, the web application starts throwing exceptions to the users who are beyond the capacity of the database. And every time you create a new connection and close a connection, you are using valuable resources. To overcome these situations, you can use a connection pool object.
A connection pool object will instantiate the connections to the backend database server. The connection pool object knows the maximum number of concurrent connections that the database can handle and accordingly is coded to instantiate connections within that capacity. A connection pool object can also be configured to have a minimum number of connections always open for use. This way, you can quickly get a connection from the pool whenever you want. And after using the connection, instead of closing the connection, it is released back to the pool.
Because of its functionality, this connection pool class can have only one instance in memory. Creating more than one instance will bring the system to a crawl if the multiple instances create more connections than the database can handle.
There are many open-source connection pools in the market today, and here is an example of Tomcat's connection pool class, called DataSource.
Singleton DataSource
package com.mbcc.mailer.examples;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class DataSourceSingleton {
private static DataSourceSingleton dataSourceSingleton = new DataSourceSingleton();
DataSource datasource = new DataSource();
private DataSourceSingleton(){
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
datasource.setPoolProperties(p);
}
public static DataSourceSingleton getInstance() {
return dataSourceSingleton;
}
public Connection getConnection() throws SQLException {
return datasource.getConnection();
}
}
Singleton Usage
package com.mbcc.mailer.examples;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SingletonExample {
public static void main(String[] args) throws SQLException {
DataSourceSingleton datasource = DataSourceSingleton.getInstance();
Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from email");
int count = 1;
while (rs.next()) {
System.out.println("Record " + count);
System.out.println(rs.getString("email_text") + ", " + rs.getString("recipient_category") + ", "
+ rs.getString("subject"));
count++;
}
rs.close();
st.close();
} finally {
if (con != null)
try {
con.close();
} catch (Exception ignore) {
}
}
}
}
Note: Although you see that the connection from the DataSourceSingleton is closed using the con.close() statement in the SingletonExample class, in reality, it is not closed but returned to the pool so that it can be reused.