同步方法在同一时刻只能锁定一个对象(即同步方法所属的对象),在并发执行中有时需要锁定的对象不止一个。另外,同步方法会形象执行效率,因此,应该尽量减少同步范围,提高程序的并发性。而同步代码块可以解决上述问题。
synchronized (<资源对象引用>){
//需要同步的代码
}
(1)资源对象引用指向的是要被锁定的资源对象,同步代码块中的代码是对资源进行的操作,这些代码在同一时刻不允许多个线程执行。
(2)线程必须先获得资源对象引用所指向的对象的锁(注意:所用的锁,因为可以存在有多个引用),才能执行同步代码块中的代码。
(3)同步代码块中的线程一样可以调用资源对象的wait方法,释放锁,进入等待池等待,一样可以使用notify()/notifyAll()去唤醒等待池中的等待线程。
(4)同步代码块执行结束,锁释放。
注意:java中资源对象的线程等待池不止一种,例如有调用wait方法后进入的wait资源等待池,也有等待获得锁的锁等待池,锁等待池中的线程在锁被释放后会自动尝试再次获取锁,而wait资源等待池中的 线程只有在wait时间到或收到notifyAll的通知才唤醒执行。当然有可能线程在wait资源等待池中被唤醒后由于没有获得锁,进入了锁等待池。
package test11;
public class MyThread extends Thread{
//该引用为资源对象
private Object resource;
public MyThread(){}
public MyThread(String name,Object resource){
this.setName(name);
this.resource = resource;
}
@Override
public void run() {
//同步语句块
synchronized (resource) {
System.out.println(this.getName()+"线程获得了锁,访问了资源!");
System.out.println(this.getName()+"线程带着锁去休息了。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"线程醒了代码执行完,锁释放了!");
}
}
}
package test11;
public class Main {
public static void main(String[] args) {
//创建资源对象
Object resource = new Object();
//创建线程,并将资源对象传给每一个线程
Thread t1 = new MyThread("t1",resource);
Thread t2 = new MyThread("t2",resource);
t1.start();
t2.start();
}
}
运行结果:
t1线程获得了锁,访问了资源!
t1线程带着锁去休息了。。。
t1线程醒了代码执行完,锁释放了!
t2线程获得了锁,访问了资源!
t2线程带着锁去休息了。。。
t2线程醒了代码执行完,锁释放了!
注意:(1)sleep方法并不会释放锁
(2)调用的wait(),notify(),notifyAll()方法都属于被锁定的资源对象 ,锁定的是谁,只能调用谁的这些方法。
线程的死锁:java中死锁的含义是:线程之间互相等待对方释放资源对象的锁,而每个线程有持有其他线程需要的锁,造成几个线程出处在永久的相互等待之中。