Java提供了一种内置的锁机制来支持原子性
每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁
内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁
内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
1,修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象
[java] view plain copy public class Test{ private int count = 0; public synchronized int add(){ count += 1; return count; } public synchronized int delete(){ count -= 1; return count; } }2,同步代码块
和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活
[java] view plain copy public class Test{ private int count = 0; public int add(){ synchronized(this){ count += 1; } return count; } public int delete(){ synchronized(this){ count -= 1; } return count; } }用synchronized关键字修饰的方法可以认为是一个横跨整个方法体的同步代码块
[java] view plain copy public class Status { private int num = 0; public void selfIncrease(){ num = num + 1; System.out.println(Thread.currentThread().getName() + "|" + num); } } [java] view plain copy public class Task implements Runnable { private Status status; public Task(Status status){ this.status = status; } public void run() { synchronized (status) { status.selfIncrease(); } } public static void main(String[] args) { Status status = new Status(); Task task = new Task(status); Thread t1 = new Thread(task); Thread t2 = new Thread(task); Thread t3 = new Thread(task); Thread t4 = new Thread(task); t1.start(); t2.start(); t3.start(); t4.start(); } }运行结果为:
[plain] view plain copy Thread-0|1 Thread-3|2 Thread-2|3 Thread-1|4修改上面的代码,每个线程运行时创建一个新的Status对象,而不是像上面的代码,4个线程共用同一个Status对象:
[java] view plain copy public class Task implements Runnable { public void run() { Status status = new Status(); synchronized (status) { status.selfIncrease(); } } public static void main(String[] args) { Task task = new Task(); Thread t1 = new Thread(task); Thread t2 = new Thread(task); Thread t3 = new Thread(task); Thread t4 = new Thread(task); t1.start(); t2.start(); t3.start(); t4.start(); } }由于充当锁的对象实例不一定是同一个对象(hashcode不同),同步失效:
[plain] view plain copy Thread-0|1 Thread-1|1 Thread-3|1 Thread-2|1因此同步代码块中充当锁的对象必须为同一个对象
[java] view plain copy public class Task implements Runnable { private Status status; public Task(Status status){ this.status = status; } public void run() { synchronized (status) { System.out.println("Thread lock"); System.out.println("Thread:" + status.getNum()); System.out.println("Thread over"); } } public static void main(String[] args) { Status status = new Status(); Task task = new Task(status); Thread t = new Thread(task); t.start(); //synchronized(status){ System.out.println("Main"); status.setNum(1); System.out.println("Main:" + status.getNum()); //} } }运行结果为:
[plain] view plain copy Main Thread lock Main:1 Thread:1 Thread over从运行结果可以看出,在Thread线程锁定status对象的时候,Main线程在Thread线程释放锁对象前依然能够修改status对象的num域,说明锁没有生效
Main线程中没有对status对象进行同步,故在Thread线程锁定status对象的时候不需要阻塞,可以直接操作status对象,因此所有使用同步对象的地方都必须进行同步
修改方式为:Task类的main方法中,在操作status对象时进行同步(去掉代码中的注释部分)
如果锁对象为静态变量,或使用synchronized关键字修饰静态方法,则锁对象为Class对象
[java] view plain copy public class Status { private static int num = 0; public synchronized static void selfIncrease(){ num = num + 1; System.out.println(Thread.currentThread().getName() + "|" + num); } } [java] view plain copy public class Task implements Runnable { public void run() { Status.selfIncrease(); } public static void main(String[] args) throws Exception { Task task = new Task(); Thread t1 = new Thread(task); Thread t2 = new Thread(task); Thread t3 = new Thread(task); Thread t4 = new Thread(task); t1.start(); t2.start(); t3.start(); t4.start(); } }运行结果如下:
[plain] view plain copy Thread-0|1 Thread-1|2 Thread-3|3 Thread-2|4相当于:
[java] view plain copy public class Status { private static int num = 0; public static void selfIncrease(){ synchronized(Status.class){ num = num + 1; System.out.println(Thread.currentThread().getName() + "|" + num); } } }或是:
[java] view plain copy public class Status { private static int num = 0; private static Object lock = new Object(); public static void selfIncrease(){ synchronized(lock){ num = num + 1; System.out.println(Thread.currentThread().getName() + "|" + num); } } }