Java中线程同步是利用了加锁解锁方式实现的。加锁解锁之间的代码块为临界区,任何时候临界区只能有一个线程执行。
Java中使用了一个关键字对一个对象加锁:synchronized
以下例子证明了锁的应用:
package MyThread;
public class MyLock {
public static void main(String[] args) throws Exception {
Thread add = new AddThread();
Thread dec = new DecThread();
add.start();
dec.start();
add.join();
dec.join();
System.out.println(Counter.count);
}
}
class Counter {
public static final Object lock = new Object();
public static int count = 0;
}
class AddThread extends Thread {
public void run() {
for (int i=0; i<100; i++) {
synchronized(Counter.lock) {
Counter.count += 1;
System.out.println(Counter.count);
}
}
}
}
class DecThread extends Thread {
public void run() {
for (int i=0; i<100; i++) {
synchronized(Counter.lock) {
Counter.count -= 1;
System.out.println(Counter.count);
}
}
}
}
最终的结果为0。
synchronized(Counter.lock) { // 获取锁
...
} // 释放锁
该代码块结束后自动施法锁。
加锁解锁操作虽然解决了同步问题,但是并不能并发执行,因此效率下降。
注意:在操作同意对象时,加的锁必须是同一对象:
synchronized(Counter.lock1) {
Counter.count += 1;
System.out.println(Counter.count);
}
synchronized(Counter.lock2) {
Counter.count -= 1;
System.out.println(Counter.count);
}
上述例中描述错误,两者操作同一变量时加入的锁不一致,导致锁机制完全没有发挥效果,结果与不加锁一致。
但如果操作不同变量但加上同一把锁,会造成大大降低了执行效率。例如:
class AddStudentThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
synchronized(Counter.lock) {
Counter.studentCount += 1;
}
}
}
}
class DecStudentThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
synchronized(Counter.lock) {
Counter.studentCount -= 1;
}
}
}
}
class AddTeacherThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
synchronized(Counter.lock) {
Counter.teacherCount += 1;
}
}
}
}
class DecTeacherThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
synchronized(Counter.lock) {
Counter.teacherCount -= 1;
}
}
}
}
上述四个类中操作的是同一锁,但操作的变量不统一,于是造成效率降低。
因此对锁的使用需要:同一把锁操作同一变量。不同的锁操作不同的变量。
假如锁住的只是一条赋值语句,则需要进行锁操作吗?
答案是不需要,因为只需要赋值一次,没有存在同步问题。
public void set(int m) {
synchronized(lock) {
this.value = m;
}
}
就可修改为
public void set(int m) {
this.value = m;
}
synchronized关键字还可以修饰方法,当它修饰方法时,表示整个方法内部是线程同步块,而加锁对象是this,即该对象的实例。另外当修饰静态方法时,被锁住的是该类的Class。
public synchronized void add(int n) { // 锁住this
count += n;
} // 解锁
等同于:
public void add(int n) {
synchronized(this) { // 锁住this
count += n;
} // 解锁
}
而:
public synchronized static void test(int n) {
...
}
等同于:
public class Counter {
public static void test(int n) {
synchronized(Counter.class) {
...
}
}
}