同步方法
使用synchronized修饰的方法,就叫做同步方法,其固定格式如下:
public [static] synchronized 返回值类型 同步方法() {
可能会产生线程安全问题的代码
}
注意事项:
- 同步方法可以是普通成员方法,也可以是static静态方法
- 普通成员同步方法,默认锁对象为this,即当前方法的调用对象
- static静态同步方法,默认锁对象是当前类的字节码对象(一个类有且只有一个)
类的字节码对象:类名.class,固定用法(当前记着即可,后续反射章节会学习)
案例1:普通同步方法
创建子线程1,调用100次普通方法print1(逐字输出 “好好学习”)
创建子线程2,调用100次普通方法print2(逐字输出 “天天向上”)
要求,两个子线程在执行方法的过程中,不会被另一个线程打断。
class Printer {
//普通同步方法: 锁对象默认为this
public synchronized void print1() {
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.println();
}
public void print2() {
//同步代码块,也使用this作为锁对象
//测试时,可以注释同步代码块,或使用其他锁对象,然后观察程序运行效果
//synchronized (Printer.class) {
synchronized (this) {
System.out.print("努");
System.out.print("力");
System.out.print("学");
System.out.print("习");
System.out.println();
}
}
}
public class Test16_Funtion {
public static void main(String[] args) {
//准备一个对象
final Printer p = new Printer();
//创建子线程1,输出100次 "好好学习"
Thread th1 = new Thread() {
@Override
public void run() {
for(int i = 0; i < 100; i++)
p.print1();
}
};
//创建子线程2,输出100次 "天天向上"
Thread th2 = new Thread() {
@Override
public void run() {
for(int i = 0; i < 100; i++)
p.print2();
}
};
th1.start();
th2.start();
}
}
测试效果:
- print2方法不使用同步代码块,或不使用this作为锁对象,会出现输出混乱的情况,线程没有实现同步(上锁失败)
- print2方法使用同步代码块,且用this作为锁对象,成功实现线程同步
案例2:静态同步方法
将上述案例中的普通同步方法,修改为静态同步方法,实现原有功能。
class Printer {
// ...省略print1() print2()
//static静态同步方法: 锁对象默认为当前类字节码对象
public static synchronized void print3() {
System.out.print("天");
System.out.print("天");
System.out.print("向");
System.out.print("上");
System.out.println();
}
public void print4() {
//同步代码块,使用当前类字节码对象作为锁对象
//注释掉同步代码块,运行测试,观察效果
//不使用当前类字节码对象作为锁对象,运行测试,观察效果
//synchronized (this) {
synchronized (Printer.class) {
System.out.print("努");
System.out.print("力");
System.out.print("学");
System.out.print("习");
System.out.println();
}
}
}
public class Test16_Funtion {
public static void main(String[] args) {
//准备一个对象
final Printer p = new Printer();
//创建子线程1,输出100次 "好好学习"
Thread th1 = new Thread() {
@Override
public void run() {
for(int i = 0; i < 100; i++)
Printer.print3();
}
};
//创建子线程2,输出100次 "天天向上"
Thread th2 = new Thread() {
@Override
public void run() {
for(int i = 0; i < 100; i++)
p.print4();
}
};
th1.start();
th2.start();
}
}
运行测试:
按照print4方法中描述进行测试,验证结论:
static静态同步方法: 锁对象默认为当前类字节码对象(类名.class)
线程状态图为: