每个对象都有个监视器,访问同步方法的线程会得到这个对象锁,其他线程只能等待
如果访问的是静态的同步方法,获得的实际上是相应的java.lang.Class的对象锁
同步的另一种写法是同步块
public void addName(String name) { synchronized(this) {//必须指明要获得哪个对象的锁,通常用于更细粒度的控制并发 lastName = name; nameCount++; } nameList.add(name); }
但同步块中要小心调用其他对象
一个线程在已经拥有这个对象锁的情况下,当然可以多次申请获取对象锁
也就是调用一个同步方法的时候,可以调用同一个对象的另一个同步方法,而无需等待
原子操作是说中间不能停止的操作,或者发生,或者不发生
以下两种属于原子读写:
1.对引用变量的读写,对原始类型的读写(但不包括long和double)
2.volatile变量(包括long,double)
原子操作无需考虑多线程错误
如果调用一个同步方法的时候,调用另一个对象的同步方法
恰巧同时,另外一个线程反过来调用,这时候肯定会死锁
两线程都拥有一个对象锁,却互相等待对方先释放
如果某个资源要消耗很长时间,而一个线程频繁大量的调用这个资源,会让别的线程经常得不到资源,进入饥饿状态
有这种线程,如果得不到资源就做出某种反应,恰巧也有另一个同样的线程相遇了
好比两个人走到了一块,你往左了他也往左,你往右他也往右,如此反复,进入活锁状态
public void guardedJoy() { // Simple loop guard. Wastes // processor time. Don't do this! while(!joy) {}//一个长期占用cpu的示例 System.out.println("Joy has been achieved!"); }
public synchronized void guardedJoy() {//推荐使用同步方法 // This guard only loops once for each special event, which may not // be the event we're waiting for. while(!joy) { try { wait();//并且在循环中加入wait() } catch (InterruptedException e) {} } System.out.println("Joy and efficiency have been achieved!"); }
当调用wait()时,会要求获得对象锁,执行wait()后,会自动释放对象锁并进入暂停状态
如果另一个线程获得这个对象锁并调用了notifyAll(),会唤醒所有等待此锁的线程
public synchronized notifyJoy() { joy = true; notifyAll(); }
第二个线程释放锁后,第一个线程则会重新获得锁,并恢复到刚才wait()的地方继续执行
notify适用于多个线程同一动作的话,随便选一个
public class Drop { // Message sent from producer // to consumer. private String message; // True if consumer should wait // for producer to send message, // false if producer should wait for // consumer to retrieve message. private boolean empty = true; public synchronized String take() {//同步方法 // Wait until message is // available. while (empty) { try { wait(); } catch (InterruptedException e) {} } // Toggle status. empty = true; // Notify producer that // status has changed. notifyAll(); return message; } public synchronized void put(String message) { // Wait until message has // been retrieved. while (!empty) { try { wait(); } catch (InterruptedException e) {} } // Toggle status. empty = false; // Store message. this.message = message; // Notify consumer that status // has changed. notifyAll(); } }
集合框架中已经有一些并发安全的结构,无需重复编码了
如果一个对象是不可更改的,当然无需考虑并发:
1.去掉所有setter方法
2.所有字段都是private final
3.不允许覆盖方法,可以声明为final类,或者改成private构造工厂模式得到实例
4.如果字段是个引用了可更改的其他对象,那么,
不要提供修改那个对象的方法,
不要传递自己给那个对象
不要存储那个对象的引用,必要的话,存储一个深拷贝
5.需要的话,生成一个自己的深拷贝,而不是本身
final public class ImmutableRGB {//一个无需考虑并发的类 // Values must be between 0 and 255. final private int red; final private int green; final private int blue; final private String name; private void check(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException(); } } public ImmutableRGB(int red, int green, int blue, String name) { check(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } public int getRGB() { return ((red << 16) | (green << 8) | blue); } public String getName() { return name; } public ImmutableRGB invert() { return new ImmutableRGB(255 - red,//返回一个新的对象 255 - green, 255 - blue, "Inverse of " + name); } }