Synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
其他线程 必须等待当前线程执行完该方法 / 代码块后才能执行该方法 / 代码块(阻塞型)。
总是,如果一个方法或者一个代码块被设置对象锁或者类锁,那它执行之前就必须要获得这个锁,如果锁被占用,就等待这个锁被释放之后再去获取。
写一个测试类:
class SynTest {
public void test1() {
for (int i = 0; i < 5; i++) {
System.out.println("*****"+i + ",test1*****");
}
}
public void test2() {
for (int i = 0; i < 5; i++) {
System.out.println("====="+i + ",test2=====");
}
}
}
开两个线程:
public class Test {
public static void main(String[] args) {
final SynTest st = new SynTest();
Thread t1 = new Thread(new Runnable() {
public void run() {
st.test1();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
st.test2();
}
});
t1.start();
t2.start();
}
}
那么此时的执行顺序是不确定的,每次运行都可能产生不同的结果。
// 运行结果1
=====0,test2=====
=====1,test2=====
*****0,test1*****
=====2,test2=====
=====3,test2=====
=====4,test2=====
*****1,test1*****
*****2,test1*****
*****3,test1*****
*****4,test1*****
// 运行结果 2
=====0,test2=====
*****0,test1*****
=====1,test2=====
=====2,test2=====
=====3,test2=====
=====4,test2=====
*****1,test1*****
*****2,test1*****
*****3,test1*****
*****4,test1*****
// 运行结果 3
*****0,test1*****
*****1,test1*****
*****2,test1*****
=====0,test2=====
*****3,test1*****
*****4,test1*****
=====1,test2=====
=====2,test2=====
=====3,test2=====
=====4,test2=====
对象锁
用 synchronized 修饰实例方法 or 用 synchronized(this) 修饰代码块:
class SynTest {
public synchronized void test1() {
for (int i = 0; i < 5; i++) {
System.out.println("*****"+i + ",test1*****");
}
}
public void test2() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println("====="+i + ",test2=====");
}
}
}
}
main() 不变,运行结果:
*****0,test1*****
*****1,test1*****
*****2,test1*****
*****3,test1*****
*****4,test1*****
=====0,test2=====
=====1,test2=====
=====2,test2=====
=====3,test2=====
=====4,test2=====
两个线程调用的是同一个类中的方法,且这两个方法都设了对象锁,当第一个线程运行时,会对这个实例上锁,此时另一个线程要调用这个实例中的方法,必须要获得该实例的锁,于是它一直等到第一个线程运行结束后,锁被释放之后才能运行。
若将main() 方法做稍许修改,使线程1和线程2调用的不是同一个实例的方法,则此时两线程互不影响。
类锁
用 synchronized 修饰静态方法 or 用 synchronized(this) 修饰代码块:
class SynTest {
public static synchronized void test1() {
for (int i = 0; i < 5; i++) {
System.out.println("*****"+i + ",test1*****");
}
}
public void test2() {
synchronized (SynTest.class) {
for (int i = 0; i < 5; i++) {
System.out.println("====="+i + ",test2=====");
}
}
}
}
public class Test {
public static void main(String[] args) {
SynTest st1 = new SynTest();
Thread t1 = new Thread(new Runnable() {
public void run() {
SynTest.test1();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
st1.test2();
}
});
t1.start();
t2.start();
}
}
运行结果:
*****0,test1*****
*****1,test1*****
*****2,test1*****
*****3,test1*****
*****4,test1*****
=====0,test2=====
=====1,test2=====
=====2,test2=====
=====3,test2=====
=====4,test2=====
test1()和test2()方法中的代码块都使用了类锁。当线程1开始执行,SynTest 类锁被占用,在其执行结束之前,线程2因为拿不到类锁会一直等待,当线程1执行结束,类锁被释放,线程2占用类锁,开始执行 test2()方法。
修改如下:
class SynTest {
public static synchronized void test1() {
for (int i = 0; i < 5; i++) {
System.out.println("*****"+i + ",test1*****");
}
}
public void test2() {
for (int i = 0; i < 3; i++) {
System.out.println("#####"+i + ",test2#####");
}
synchronized (SynTest.class) {
for (int i = 0; i < 3; i++) {
System.out.println("====="+i + ",test2=====");
}
}
}
}
main() 不变,运行结果:
// 前 8 行随机组合,但后 3 行固定
#####0,test2#####
#####1,test2#####
*****0,test1*****
#####2,test2#####
*****1,test1*****
*****2,test1*****
*****3,test1*****
*****4,test1*****
=====0,test2=====
=====1,test2=====
=====2,test2=====