当前位置:编程学习 > JAVA >>

Java并发编程详解之 线程安全和对象共享

一、简单介绍
在我们用框架编程的过程中,经常不用太关注多线程问题,这是因为例如Servlet和RMI(Remote Method Invocation,远程方法调用),RMI使得代码能够调用其他JVM中的运行的对象。这些框架负责解决一些细节问题,例如请求管理、线程创建、负载均衡,并在正确的时刻将请求分发给正确的应用程序组件。编写Servlet的开发人员不需要了解有多少请求在同一时刻要被处理,也不需要了解套接字的输入流或输出流是否被阻塞。当调用Servlet的service方法来响应Web请求时,可以以同步方式来处理这个请求,就好像它是一个单线程程序。这种方式可以简化组件的开发,并缩短掌握这种框架的学习时间,但我们想学好编程,还是需要学习并发编程

1、编程的一些原则

在编写并发应用程序时,一种正确的编程方法就是:首先使代码正确运行,然后再提高代码的速度。即便如此,最好也只是当性能测试结果和应用需求告诉你必须提高性能,以及测量结果表明这种优化在实际环境中确实能带来性能提升时,才进行优化

2、活跃性问题

多线程中提到的活跃性问题,指的就是让线程处理死锁或活锁,就是各个线程阻塞不执行,当某个操作无法继续执行下去时,这样就导致了活跃性问题

平时编程中应该怎样做到这句话?:框架通过在框架线程中调用应用程序代码将并发性引入到程序中。在代码中将不可避免地访问应用程序状态,因此所有访问这些状态的代码路径都必须是线程安全的

3、一般并发错误的修复方法

如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误。有三种方式可以修复这个问题:

(1)不在线程之间共享该状态变量

(2)将状态变量修改为不可变的变量

(3)在访问状态变量时使用同步

4、线程安全类和线程安全程序

完全由线程安全类构成的程序并不一定就是线程安全的,而在线程安全类中也可以包含非线程安全的类

二、线程安全性
当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的

1、无状态对象
一个无状态的Servlet


[java]
import java.math.BigInteger; 
import javax.servlet.*; 
 
import net.jcip.annotations.*; 
 
@ThreadSafe 
public class StatelessFactorizer extends GenericServlet implements Servlet { 
 
    public void service(ServletRequest req, ServletResponse resp) { 
        BigInteger i = extractFromRequest(req); 
        BigInteger[] factors = factor(i); 
        encodeIntoResponse(resp, factors); 
    } 

import java.math.BigInteger;
import javax.servlet.*;

import net.jcip.annotations.*;

@ThreadSafe
public class StatelessFactorizer extends GenericServlet implements Servlet {

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        encodeIntoResponse(resp, factors);
    }
}大多数Servlet都是无状态的,从而极大地降低了在实现Servlet线程安全性时的复杂性,只有当Servlet在处理请求时需要保存一些信息,线程安全性才会成为一个问题

无状态对象一定是线程安全的

下面的Servlet 就不再是无状态的了

在没有同步的情况下统计已处理请求数量的Servlet(不要这么做)


[java]
import java.math.BigInteger; 
import javax.servlet.*; 
import net.jcip.annotations.*; 
 
@NotThreadSafe 
public class UnsafeCountingFactorizer extends GenericServlet implements Servlet { 
    private long count = 0; 
 
    public long getCount() { 
        return count; 
    } 
 
    public void service(ServletRequest req, ServletResponse resp) { 
        BigInteger i = extractFromRequest(req); 
        BigInteger[] factors = factor(i); 
        ++count; 
        encodeIntoResponse(resp, factors); 
    } 
 
    void encodeIntoResponse(ServletResponse res, BigInteger[] factors) { 
    } 
 
    BigInteger extractFromRequest(ServletRequest req) { 
        return new BigInteger("7"); 
    } 
 
    BigInteger[] factor(BigInteger i) { 
        // Doesn't really factor  
        return new BigInteger[] { i }; 
    } 

import java.math.BigInteger;
import javax.servlet.*;
import net.jcip.annotations.*;

@NotThreadSafe
public class UnsafeCountingFactorizer extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        ++count;
        encodeIntoResponse(resp, factors);
    }

    void encodeIntoResponse(ServletResponse res, BigInteger[] factors) {
    }

    BigInteger extractFromRequest(ServletRequest req) {
        return new BigInteger("7");
    }

    BigInteger[] factor(BigInteger i) {
        // Doesn't really factor
        return new BigInteger[] { i };
    }
}

count是这个对象的状态,虽然递增操作++count是一种紧凑的语法,使其看上去只是一个操作,但是这个操作并非原子的,实际上,它包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。这是一个“读取-修改-写入”的操作序列,并且其结果状态依赖于之前的状态。这样会导致不恰当的执行时序而出现不正确的结果,这种情况称为竞态条件

解决方法:

1、使用AtomicLong类型的变量来统计已处理请求的数量


[java]

import java.math.BigInteger; 
import java.util.concurrent.atomic.*; 
import javax.servlet.*; 
import net.jcip.annotations.*; 
 
@ThreadSafe 
public class CountingFactorizer extends GenericServlet implements Servlet { 
    private final AtomicLong count = new AtomicLong(0); 
 
    public long getCount() { return count.get(); } 
 
    public void service(ServletRequest req, ServletResponse resp) { 
        BigInteger i = extractFromRequest(req); 
        BigInteger[] factors = factor(i); 
        count.incrementAndGet(); 
        encodeIntoResponse(resp, factors); 
    } 

import java.math.BigInteger;
import java.util.concurrent.atomic.*;
import javax.servlet.*;
import net.jcip.annotations.*;

@ThreadSafe
public class Co

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,