线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。同步的方法通过锁来实现,每个对象都有且仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
对于同步的方法,这里先主要学习使用synchronized关键字修饰的方法和锁对象Lock
1、 Synchronized
Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁,也只有在这时,对象锁才起作用。一个对象只有一个锁。如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。也就是说任何其他线程都不能进入该对象上的synchronized方法或代码块,直到持锁线程退出了synchronized同步方法或代码块该锁被释放。(同步只能是同步方法,变量和类不能同步)
package Synchronized20170412;
/**
* 线程同步的运用
*/
public class Synchronized20170412 {
public static void main(String[] args) {
Fun f = new Fun();
NewThread new_thread = new NewThread(f);
Thread thread1 = new Thread(new_thread);
thread1.start();
Thread thread2 = new Thread(new_thread);
thread2.start();
}
}
class Fun {
private int total = 0;
public int getTotal() {
return total;
}
/**
* 用同步方法实现
*
* @param money
*/
public synchronized void add(int i) {
total += i;
}
/**
* 用同步代码块实现
*
* @param money
*/
public void add1(int i) {
synchronized (this) {
total += i;
}
}
}
class NewThread implements Runnable {
private Fun f;
public NewThread(Fun f) {
this.f = f;
}
public void run() {
//System.out.println(Thread.currentThread().getName() + " Total = " + f.getTotal());
for (int i = 0; i < 5; i++) {
f.add1(10);
//f.add(10);
System.out.println(Thread.currentThread().getName() + " i = " + i + " Total = " + f.getTotal());
}
}
}
结果如下:
可以看到线程0和线程1之间只有等待对方释放锁后,自己才能获得锁并在Total上加10,他们之间的操作不会有冲突.
2、 Lock
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作
与synchronized的区别:
Lock可以使用Condition进行线程之间的调度,Synchronized则使用Object对象本身的notify, wait, notityAll调度机制。
Condition(即对象监视器)是Java5以后出现的机制,它有更好的灵活性,在一个对象里面可以有多个Condition,则线程可以注册在不同的Condition,从而可以有选择性的调度线程,更加灵活。他并不是一种替代内置加锁的方法,而是当内置加锁机制不适用时,作为一种可选择的高级功能,lock接口提供了一种无条件的、可轮询的、定时的以及可中断的锁获取机制,所有的加锁和解锁方法都是显示的
Synchronized就相当于整个对象只有一个单一的Condition(即该对象本身)所有的线程都注册在它身上
package Lock20170412;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Lock20170412 {
public static void main(String[] args) {
Fun f = new Fun();
NewThread new_thread = new NewThread(f);
Thread thread1 = new Thread(new_thread);
thread1.start();
Thread thread2 = new Thread(new_thread);
thread2.start();
}
}
class Fun {
private int total = 0;
public int getTotal() {
return total;
}
public void add(int i) {
total += i;
}
}
class NewThread implements Runnable {
private Fun f;
private Lock lock = new ReentrantLock();// 锁对象
public NewThread(Fun f) {
this.f = f;
}
public void run() {
lock.lock();// 得到锁
try {
for (int i = 0; i < 5; i++) {
f.add(10);
System.out.println(Thread.currentThread().getName() + " i = " + i + " Total = " + f.getTotal());
}
} finally {
lock.unlock();// 释放锁
}
}
}
结果如下:
可以看到线程0执行完毕后才释放锁,线程1得到锁后才开始执行