Java锁
Java为了维护线程安全提供了关键字synchronized
,可以用两种方式设计锁
public class MyThread implements Runnable{
Integer n = 3;
@Override
public void run() {
synchronized (n) {
n--;
System.out.println(n);
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
new Thread(t).start();
new Thread(t).start();
}
}
或者是
public class Add{
Integer n = 3;
public synchronized void minus() {
n--;
System.out.println(n);
}
}
直接修饰方法,需要注意的是,如果修饰的是静态方法,则意味着锁的对象是整个类。
锁的本质在于将代码块变成一个原子操作,并置对象的锁符号位,获取对象的唯一访问权,阻塞其他访问该对象的线程。锁池和等待池
- Java中任何对象都可以被锁,但基本数据类型是不能被锁的。
- Java规定构造函数执行是互斥的,不需要手动去锁,线程同步方法
- 在线程中锁住某个对象,并不意味着对象不能进行任何操作,锁的根本在于对代码块上锁。如果另一个线程,不需要检查对象的锁,则仍然可以在对象被锁的同时访问对象。所以在线程中如果涉及到可变对象的修改操作,同时还有该对象的访问操作,则需要对修改和访问操作同时锁。
Monitor模式
对于类中每个访问成员变量的方法,都直接用synchronized加锁,使得线程安全。但问题在于锁的粒度太粗,导致实际调用中并行效率太差。可以修改为细粒度的锁,比如在具体访问变量的语句上加锁,这样既可以保证运行效率和线程安全。
死锁
当出现如下情况
//线程一
synchronized(a){
synchronized(b){
……
}
}
//线程二
synchronized(b){
synchronized(a){
……
}
}
当两个线程同时运行时,会出现程序无法进行下去的情况。当线程一获取了对象a的锁,需要b的锁的同时。线程二获取了对象b的锁,需要a的锁。这样两个线程占用了对方运行需要的锁,互相等待对方释放,导致都无法运行,这种情况就称为死锁现象。
wait挂起
java中为每个对象都提供了wait()
方法,用于对该对象加锁的代码块内,会释放掉当前代码块对该对象锁的占用,使该线程进入等待池。当其他线程调用object.notify()
时,会随机从该对象的等待池中选取线程唤醒,并把对象的锁赋给它。或是使用object.notifyAll()
,会唤醒所有线程使它们自主竞争。
这里与sleep方法不同,sleep只会休眠线程,不会释放所占用的锁。