笔记二:独占

考虑个问题,为什么要独占?
假如你的对象状态永远是不会改变的,那么就不用考虑这个问题了。
达到独占的基本策略:
一.确保对象永远不会出现不一致状态的情况
例如保证对象的无状态性:

class stateless{
public int add(int a,int b){
retun a+b;
}
}

或者可以把属性引用申明为为final
class TT{
private final int a;

TT(int a){
this.a =a ;
}

public int addA(int b){return a+b;}

}


用不变的对象来判断状态
flyweight模式
在构造函数之前不能访问对象数据

二.加锁保证一个对象同时只能被一个线程访问
锁其实是个对象,锁监视器监视该对象是否有线程进入

如何进行集合遍历(对集合操作的可能无限多,不可能每个方法都加锁):
1)聚合锁
class testarray {
private int a[];

public synchronized void printAll() {
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);
}

}


2)索引化遍历和客户端锁

synchronized(v){
for(int i=0;i<v.size();i++)
{
v.get(i);
}
}


这样做持有锁时间太长,有活跃性问题
改进:

object[] objs = null;
synchronized(v){
objs = new Object[v.size()];
for(int i=0;i<v.size();i++)
{
objs[i] = v.get(i);
}
}

for(int i=0;i<objs.length;i++)
{
objs[i].operator();
}



不过这种方式以破坏封装为原则,假如这个类被改变了,那么很有可能出问题。

3)失败即放弃
就是每次对集合添加,删除对象,都将更改版本号。遍历的时候比较版本号,假如对不上就失败。

synchronized(List.this){
if(currVer !=version)
throw Exception();
}


单例的问题:
方案一:非延迟加载单例类
Java代码
public class Singleton {

  private Singleton(){}

  private static final Singleton instance = new Singleton();

  public static Singleton getInstance() {
    return instance;   
  }
}

public class Singleton {

  private Singleton(){}

  private static final Singleton instance = new Singleton();

  public static Singleton getInstance() {
    return instance;   
  }
}


方案二:简单的同步延迟加载
Java代码
public class Singleton {

  private static Singleton instance = null;

  public static synchronized Singleton getInstance() {
    if (instance == null)
      instance = new Singleton();
    return instance;   
  }

}

public class Singleton {

  private static Singleton instance = null;

  public static synchronized Singleton getInstance() {
    if (instance == null)
      instance = new Singleton();
    return instance;   
  }

}


方案三:双重检查成例延迟加载
目的是避开过多的同步,
但在Java中行不通,因为同步块外面的if (instance == null)可能看到已存在,但不完整的实例。
JDK5.0以后版本若instance为volatile则可行
Java代码
public class Singleton {

  private static Singleton instance = null;

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

}

public class Singleton {

  private static Singleton instance = null;

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

}


方案四:类加载器延迟加载
Java代码
public class Singleton {

  private static class Holder {
   static final Singleton instance = new Singleton();
  }

  public static Singleton getInstance() {
    return Holder.instance;   
  }

}


死锁:
线程1持有A锁正等待进入B锁。线程2持有B锁正等待进入A锁。
如果线程1和2都以同样的顺序,例如先A后B,或者先B后A那么就不会死锁。
例:

[img]http://dl.iteye.com/upload/attachment/193777/f75a2cb2-e97b-3224-bc9c-29486931a7ee.jpg[/img]
解决方法:

[img]http://dl.iteye.com/upload/attachment/193779/b9801882-6590-34ac-858e-d139b0554a80.jpg[/img]

java存储模型:(请认真阅读JAVA语言规范第17章)
1.假设每个线程都运行在不同的CPU上
2.模型容许一个线程不知道被另外一个线程处理的值的情况。
3.模型不会明确指定上述策略是由编译器,CPU,缓存控制器还是其他机制来执行。
主要围绕的问题:
原子性 非long和double的原子类型,long和double加volatile也可保证原子性。
可见性 同步和volatile都能保证可见性
顺序化 保证同步方法和块的相关顺序


三.隐藏或者限制对象的使用权限,结构性的保证只能被一个线程使用该对象。
通过封装技术来实现
1.跨方法限制
1).在方法未必泄露引用。
2).对象只存在于一次会话中
3).调用者或者接受者拷贝对象,而非直接操作引用
2.线程内限制
1)一个线程一个会话
2)一个线程维护一个私有变量
3).ThreadLocal
3.对象内限制
适配器
子类化
4.组限制,对一组资源访问的问题,例如令牌环


[img]http://dl.iteye.com/upload/attachment/193816/6d9bc191-b408-3c64-9586-146e1dd77c8b.jpg[/img]


[img]http://dl.iteye.com/upload/attachment/193818/036c3d53-1f62-3d28-941b-4531c3109b2b.jpg[/img]

[img]http://dl.iteye.com/upload/attachment/193820/142cede6-90b6-3f2e-8bb7-7f0f158cb314.jpg[/img]


四:构造和重构类
重构需要处理的问题:
1.锁占用太长时间
2.锁太多
3.一个锁保护多个方面
4.锁只能支持很多的并发数
5.锁没起到预想效果

解决方案:
1.减少同步(看情况进行处理,如果不能接受陈旧数据,需要对存储方法进行同步)
2.双重检测,减少同步。如果检测的是对象,例如if (instance==null),需要有volatile关键字,否则有可见性问题。
3.分解同步(分解类,分解锁)
4.只读适配器,弱化功能,对原生类进行包装,例如Collections.UnmodifiableCollection
5.写拷贝
内部写拷贝,一般用于本次改变,下一次通知才生效。

[img]http://dl.iteye.com/upload/attachment/193836/87d75146-f803-3a49-97aa-f36b52f79573.jpg[/img]

[img]http://dl.iteye.com/upload/attachment/193838/881f3dc0-b445-324e-9e48-af699b287e40.jpg[/img]
6.乐观更新(把同步的粒度缩减到最小)
1)得到当前线程状态的一个拷贝(持有锁的时候)
2)创建一个新的对象(不持有锁)
3)只有在老状态被获取后并且没有改变才能被更新。
比较并交换

[img]http://dl.iteye.com/upload/attachment/193840/a3c80976-1c59-311b-b61e-ed971d5566ec.jpg[/img]
虽然JAVA不能访问底层CAS代码,但是原则上编译器会优化该代码。

7.开放容器
假如访问一个对象的A,B部分,分别需要获取不同的锁。那么有可能死锁。可以在容器上加一个大锁,只有获取了该锁才可以获取A锁,或者B锁。

[img]http://dl.iteye.com/upload/attachment/193842/93a71e06-f6fa-33d4-92ba-5eaa92bc46a4.jpg[/img]


五。锁工具
解决同步不能回退
改变锁的语义
可以做同步的访问控制
方法块内严格限制


例如:Mutex类
锁管理器实现锁的顺序化
读写锁,当读的比例远远大于写
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值