每个对象都有个监视器,访问同步方法的线程会得到这个对象锁,其他线程只能等待
如果访问的是静态的同步方法,获得的实际上是相应的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);
}
}