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只会休眠线程,不会释放所占用的锁。