Java线程安全问题之静态变量、实例变量、局部变量

静态变量:非线程安全

静态变量即类变量,位于方法区,为所有对象共享,共享一份内存,一旦静态变量被修改,其他对象均对修改可见。

实例变量:

单例模式线程非安全;非单例模式线程安全

局部变量:

每个线程执行时将会把局部变量放在各自栈帧中,线程件不共享,故不存在线程安全问题

2016/1/18 posted in  Java

Java 单例模式

public class Singleton
{
    private volatile static Singleton singleton = null;
    private Singleton()
    {
    }

    public static Singleton getInstance()
    {
        if (singleton == null) 
        {
            synchronized (Singleton.class) {
                if (singleton == null)
                {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

或者

使用JVM本身机制保证了线程安全问题;由于SingletonHolder是私有的,除了getInstance()之外没有办法访问它,因此它只有在getInstance()被调用时才会真正创建;同时读取实例的时候不会进行同步,没有性能缺陷。

public class Singleton
{
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton()
    {
    }

    public static final Singleton getInstance()
    {
        return SingletonHolder.INSTANCE;
    }
}
2016/1/17 posted in  Java

Spring

The Spring Inversion of Control (IOC) container has three main logical components: a registry (called the ApplicationContext) of components (beans) that are available to be used by the application, a configurer system that injects objects' dependencies into them by matching up the dependencies with beans in the context. and a dependency solver that can look at a configuration of many different beans and determine how to instantiate and configure them in the necessary order.

The IoC container isnt't magic, and it has no way of knowing about Java objects unless you somehow inform it of them. When you call new, the JVM instantiates a copuy of the new object and hands it straight to you - it never goes through the configuration process. There are three ways that you can get your beans configured.

Inject your beans

The most preferable opetion is to let Spring autowire all of your beans; thie requires the least amount of code and is the most maintainable.

Use @Configurable

If you really need objects created with new to be autowired, you can use the Spring Configurable annotation along with AspectJ compile-time weaving to inject your objects. This approach inserts code into your object's constructor that alerts Spring that it's being created so that Spring can configure the new instance. This requires a bit of configuration in your build and turning on Spring's runtime configuration handlers.

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method.

2016/1/7 posted in  Java

看透Spring MVC

缓存的失效可以定期失效,也可以在数据发生变化的时候失效,如果按数据发生变化让缓存失效,还可以分粗粒度失效和细粒度失效。

像快递送货一样,货单上填写地址的规则以及怎么根据填写的内容找到客户,这就相当于IP协议,而送货时要先打电话,然后将货物送过去,最后客户签收时要签字就相当于TCP协议。

三次握手和四次挥手保证了连接的可靠性,不过这种模式也有它的缺点,首先是在传输效率上会比较低,另外三次握手的过程中客户端需要发送两次数据才可以建立连接,这种特征可能被一些别有用心的人利用,比如,发出第一次握手后就不回应第三次握手了,这时服务端会以为是第二次握手的数据在传输过程中丢失了,然后重新发送第二次握手,默认情况下会一直发送五次,如果发送五次后还收不到第三次握手则会丢弃请求。这就是DDOS攻击中的SYN Flood攻击,对于这种攻击的一种应对方法是设置第二次请求的重发次数,不过重发的次数大小也可能导致正常的请求中因为网络没有收到第二次握手而连接失败的情况。

通过TCP/IP协议、HTTP协议已经可以得到数据了,Servlet的作用是对接收到的数据进行处理并生成要返回给客户端的结果。

域名解析有很多种解析的类型,如常用的A记录和CNAME记录。A记录是将域名解析到IP(一个域名可以有多条A记录),CNAME记录是将域名解析到另一个域名(也就是作为另一个域名的别名),查找时会返回目标域名所对应的IP

ServerSocket的使用可以分为三步:
- 创建ServerSocket
- 调用创建出来的ServerSocketaccept方法进行监听。accept方法是阻塞方法,也就是说调用accept方法后程序会停下来等待连接请求,在接收到请求之前程序都不会往下走;当接收到请求后accept方法会返回一个Socket
- 使用accept方法返回的Socket与客户端进行通信

普通Socket处理请求的模式:收到请求,处理,处理好了,收下一个请求。

NIOSocket处理请求的模式:快递并不会一件一件地送,而是将很多件货一起拿去送,而且在中转站都有专门的分拣员负责按配送范围将货物分给不同的送货员,这样效率就提高了很多。

NIOSocket使用中首先要创建ServerSocketChannel,然后注册Selector,接下来就可以用Selector接受请求并处理了。

NIOSocket中服务端的处理过程可以分为5步:
1、创建ServerSocketChannel并设置相应参数
2、创建Selector并注册到ServerSocketChannel
3、调用Selectorselect方法等待请求
4、Selector接收到请求后使用selectedKeys返回SelectionKey集合
5、使用SelectionKey获取到ChannelSelector和操作类型并进行具体操作

// 恢复到初始状态
public final Buffer clear()
{
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

// 开启读的模式
public final Buffer flip()
{
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

Servlet接口
- init方法在容器启动时被容器调用(当load-on-startup设置为负数或者不设置时会在Servlet第一次用到时才被调用),只会调用一次
- getServletConfig方法用于获取ServletConfig
- service方法用于具体处理一个请求
- getServletInfo方法可以获取一些Servlet相关的信息,默认返回空字符串
- destroy主要用于在Servlet销毁(一般指关闭服务器)时释放一些资源,只会调用一次

getServletName用于获取Servlet的名字,也就是我们在web.xml中定义的servlet-namegetInitParameter方法用于获取init-param配置的参数;getInitParameterNames用于获取配置的所有init-param的名字集合;getServletContext的返回值代表的是我们这个应用本身。ServletConfigServlet级的,而ServletContextContext(也就是Application)级的。

String contextLocation = getServletConfig().getServletContext().getInitParameter("contextConfigLocation");
String servletLocation = getServletConfig().getInitParameter("contextConfigLocation");

GenericServlet

GenericServletServlet的默认实现,主要是做了三件事:
- 实现了ServletConfig接口,我们可以直接调用ServletConfig里面的方法
- 提供了无参的init方法
- 提供了log方法

GenericServlet实现了Servletinit(ServletConfig config)方法,在里面将config设置给了内部变量config,然后调用了无参的init()方法

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
public void init() throws ServletException {

}

HttpServlet

HttpServlet是用HTTP协议实现的Servlet的基类。HttpServlet主要重写了service方法。在service方法中首先将ServletRequestServletResponse转换为了HttpServletRequestHttpServletResponse,然后根据Http请求的类型不同将请求路由到了不同的处理方法

Tomcat的顶层结构

Tomcat中最顶层的容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务。Service主要包含两部分:ConnectorContainerConnector用于处理连接相关的事情,并提供Socketrequestresponse的转换,Container用于封装和管理Servlet,以及具体处理request请求。

一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以有多个Connectors(因为一个服务可以有多个连接)

Server的启动过程

await方法处理的大概逻辑是首先判断端口号port,然后根据port的值分为三种处理方法:
- port为-2,则会直接退出,不进入循环
- port为-1,则会进入一个while(!stopAwait)的循环,只有在外部调用stop方法才会退出循环
- port为其他值,则也会进入一个while(!stopAwait)的循环,不过同时会在port所在的端口启动一个ServerSocket来监听关闭命令,如果姐收到了则会使用break跳出循环

2015/12/17 posted in  Java