1、简介
1)串行编程模式的优势在于其直观性和简单性,因为它模仿了人类的工作方式:每次只做一件事情,做完之后再做另一件事情,但凡高效的人,总能在串行性与异步性之间找到合理的平衡。
2)在大多数现在操作系统中,都是以线程为基本的调度单位。如果没有明确的协同机制,那么线程将彼此独立运行,这些线程能访问相同的变量并在同一个堆上分配对象,这就需要一种比在进程间共享数据粒度更细的数据共享机制。
3)当多个线程访问某个状态变量并且其中有一个线程执行写操作的时候,必须采用同步机制来协调这些线程对变量的访问,java中的主要同步机制关键字synchronized,它提供了一种独立的加锁方式,但同步这个术语还包括volatile类型的变量,显示锁,以及原子变量
4)如果多个线程访问同一变量时没有实现同步,出现错误,有3种修复方式,不在线程之间共享该状态变量、将状态变量修改为不可变的变量、在访问状态变量时使用同步
2、线程安全性
1)当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
2)无状态的对象一定是线程安全的,因为两个线程没有共享的状态,计算过程的临时状态仅存在局部变量中,就好像他们都在访问不同的实例。无状态对象一定是线程安全的,大多数servlet是无状态的。
3)线程不安全的例子(用于生成唯一的标识,导致不正确的结果)
在并发编程中,这种由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,叫竟态条件。(书中举了一个星巴克的例子)
线程不安全:public class UnsafeServlet implments Servlet{
private long count=0;
public void service(){
++count;
System.out.println(count)
}
}
线程安全(使用原子变量类):
public class DemoServlet implements Servlet{
private final AtomicLong count=new AtomicLong(0);
private long getCount(){
return count.get();
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println(count.incrementAndGet());
}
}
延迟初始化的静态条件(不要这么做)
public class LazyinitRace{
private ExpensiveObject instance=null;
public ExpensiveObject getInstance(){
if(instance==null){
instance=new ExpensiveObject();
return instance;
}
}
}
4)内置锁,线程进入内置同步代码块之前会自动获取锁,并且退出同步代码块自动释放锁。
synchronized void method(){
}
等价于
void method(){
synchronized(){
}
}
虽然是线程安全的,但这种方法过于极端,因为多个客户端无法同时使用因式分解Servlet,服务的响应性非常低,这是一个性能问题,而不是线程安全的问题。
内置锁是可重入的,因此如果某个线程试图获得一个已经由她自己持有的锁,那么这个请求就会成功。重入的一种实现方法是就,为每一个锁关联一个获取计数值和一个线程所有者,当计数器为0是,锁认为是没有被任何线程持有,当线程请求一个未持有锁时,jvm记下锁的持有者,并且将计数值加1,如果同一个线程再次获取锁,计数值将递增,当线程退出同步代码块,计值递减,当计数值为0的时候会被释放。
注意:如果内置锁不是可重入的,那么这段代码将发生死r锁
public class Widget{
public synchronized void something(){
}
}
public class LoggingWidget extends Widget{
public synchronized void something(){
System.out.println(" hello world ");
super.doSomething();
}
}
调用子类的方法,可重入锁的是子类还是父类对象的验证:
结果验证锁的都是子类的对象。
5)http://ifeve.com/java-atomic/( Java中的Atomic包使用指南)
volatile 关键字:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
http://www.mamicode.com/info-detail-862009.html
6)当使用锁时,应该清楚代码中实现的功能,以及在执行该代码时是否需要很长的时间。无论是执行计算密集的操作,还是在执行某个可能阻塞的操作,如果持有锁的时间过长,那么都会带来活跃性或性能问题。
3、对象的共享
1)关键字synchronized不仅用于实现原子性或者确定临界区,还有另一个重要的方面就是内存的可见性。为了确保多个线程之间对内存的写入操作的可见性,必须使用同步机制。