在多线程环境下,共享数据会存在安全问题,sychronized可以保证同一时只有一个线程能进入代码块或者是这个方法。因此synchronized可以有三种用法:修饰代码块,修饰类中的普通方法(锁实例对象),修饰静态方法(锁类对象)。
下面来看下作用于普通方法的例子
实验1:手机类有两个普通同步方法:发短信和发邮件
class Phone{
public synchronized void sendEmail() throws Exception{
System.out.println("sendEmail");
}
public synchronized void sendMs() throws Exception{
System.out.println("sendMessage");
}
}
下面创建资源类phone, 通过Thread.sleep()来使得A线程先启动,在哪个线程里面调用sleep()方法就阻塞哪个线程,所以main线程到这里会堵塞住
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A") .start();
Thread.sleep(100);//人为让A先启动
new Thread(() -> {
try {
phone.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
}, "B") .start();
}
}
结果是 “sendEmail” 必定在 "sendMessage"之前,分析:两个线程对一个对象中的普通方法进行操作,一个对象只有一把锁,只有一个线程能抢到这个锁。
实验2: 手机类有1个普通同步方法(sendEmail),1个普通方法(sayHello)
public void sayHello() {
System.out.println("say hello");
public synchronized void sendEmail() throws Exception{
System.out.println("sendEmail");
}
}
这里为了看出来效果,让sendEmail方法停滞 4秒,如果sayHello被阻塞,也会有感觉。
public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
e.printStackTrace();
}
}, "A") .start();
Thread.sleep(100);//人为让A先启动
new Thread(() -> {
try {
phone.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
}, "B") .start();
}
}
结果say hello必定在sendEmail之前马上出来。结论:线程占用普通同步方法不影响别的线程使用同一实现类的其他普通方法。以此类推如果调用另外一个实现类的普通方法,更不会有什么影响。
实验3 : 两个同步方法加上static关键字编程静态同步方法。这里建了就两个资源类:phone 和phone2,分别调用一种静态同步方法
class Phone{
public static synchronized void sendEmail() throws Exception{
System.out.println("sendEmail");
}
public static synchronized void sendMs() throws Exception{
System.out.println("sendmessage");
}
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}, "A") .start();
Thread.sleep(100);
new Thread(() -> {
try {
phone2.sendMs();
} catch (Exception e) {
e.printStackTrace();
}
}, "B") .start();
}
结果是sendEmail先于sendMsg出现,结论:静态同步方法锁类。
以此类推,如果是同一个资源类调用两个静态同步方法,更是要互相冲突的。如果是静态同步方法和普通方法,是不会互相影响的,这里就不作赘述。