synchronized
例子:
我们知道,synchronized可以用来修饰一个方法或者一个代码块甚至一个类,能够保证在同一时刻最多只有一个线程执行该段代码。
public synchronized void yourMethod() {
//...
}
这时,线程获得的是成员锁,即一次只能有一个线程进入该方法,而其他线程要想在此时调用该方法,只能排队等候。只有当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入。另一个线程仍然可以访问该object中的非synchronized同步代码块。
例子:
public class CopyOfZwThread2 extends Thread {
static int tick = 0;
public synchronized void increaseN() {
tick++;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
increaseN();
sleep(30);
} catch (InterruptedException e) {
}
}
}
}
对应的创建100个线程对变量进行自增:
public class RunThread {
public static void main(String[] args) throws Exception {
ThreadVar tv = new ThreadVar();
Thread threads[] = new Thread[100];
for (int i = 0; i < threads.length; i++)
// 建立100个线程
threads[i] = new CopyOfZwThread2();
for (int i = 0; i < threads.length; i++)
// 运行刚才建立的100个线程
threads[i].start();
for (int i = 0; i < threads.length; i++){
threads[i].join();
// 100个线程都执行完后继续
}
System.out.println(" n= " + CopyOfZwThread2.tick);
}
}
运行完是不是发现输出并不是我们想要的 1000。Why?
其实该写法等同于下面的写法:
public void yourMethod() {
synchronized(this) {
//...
}
}
即,锁是加载当前对象上的,这里是100个线程对象。与我们期待的明显不一致。可以采用下面第二种写法:
public class CopyOfZwThread2 extends Thread {
static int tick2 = 0;
// 创建一个静态钥匙
static Object varlock = "zw";//取值任意
public void increaseN2() {
synchronized(varlock) {
tick2++;
}
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
increase();
sleep(30);
} catch (InterruptedException e) {
}
}
}
}
此时,锁是基于”钥匙“对象的,只有拿到钥匙才能对变量自增。
这里需要注意的是,synchronized后面括号里的一定要是引用类型。比如可以是Integer 类型,但不能是int。很简单,基本类型都是传值使用当然无法满足需要了。有兴趣的可以试下 int,发现会有编译错:int is not a valid type's argument for the synchronized statement。
当然,synchronized还能加在静态代码块上,下面代码一个加了synchronized,另一个没加:
synchronized public static void printA() {
System.out.println("线程" + Thread.currentThread().getName() + " come to printA");
try {
tick++;
sleep(30);
} catch (InterruptedException e) {
}
System.out.println("线程" + Thread.currentThread().getName() + " leave to printA");
}
public static void printB() {
System.out.println("线程" + Thread.currentThread().getName() + " come to printB");
try {
tick2++;
sleep(30);
} catch (InterruptedException e) {
}
System.out.println("线程" + Thread.currentThread().getName() + " leave to printB");
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
printA();
printB();
sleep(30);
} catch (InterruptedException e) {
}
}
}
最后输出部分如下:
线程53 come to printA
线程53 leave to printA
线程52 leave to printB
线程53 come to printB
线程54 come to printA
线程54 leave to printA
线程56 come to printA
线程54 come to printB (53尚未离开printB)
线程53 leave to printB
线程54 leave to printB
线程56 leave to printA
线程56 come to printB
线程57 come to printA
线程57 leave to printA
线程56 leave to printB
线程55 come to printA
线程57 come to printB
线程57 leave to printB
....
tick= 1000 (最后是 1000 意味着同步的)
tick2= 1000
从上面的输出可以看出,对静态代码块加 synchronized 实现了同步。