1 简介
synchronized在方法内,同步代码块,传入对象,使用的是对象锁,传入class对象,使用的是类锁
作用于普通方法,也是对象锁,当前对象
作用于静态方法,是类锁
2 同步方法示例
2.1两个synchronized 修饰的普通方法
public class SychTest1 {
static class Phone{
synchronized void sendEmail(){ //相当于synchromized(this)
System.out.println("EMAIL");
}
synchronized void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
//等待200
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p.sendMsg();
}).start();
}
}
执行结果
EMAIL
MSG
2.2两个synchronized 修饰的普通方法,sendEmail睡了2秒
public class SychTest2 {
static class Phone{
synchronized void sendEmail(){
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
synchronized void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p.sendMsg();
}).start();
}
}
执行结果,发现第一个线程调用的sendEmail方法虽然等待了2秒,还是先打印,因为它先获取了锁,第二个线程只能等待。它们竞争的是同一个锁对象p
EMAIL
MSG
2.3一个同步方法,一个普通方法
public class SychTest3 {
static class Phone{
synchronized void sendEmail(){
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p.sendMsg();
}).start();
}
}
执行结果
MSG
EMAIL
2.4两个同步方法,但是是两个不同的对象p和p2去调用同步方法
public class SychTest4 {
static class Phone{
synchronized void sendEmail(){
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
synchronized void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
Phone p2 = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p2.sendMsg();
}).start();
}
}
执行结果,持有的是不同的锁对象p和p2
MSG
EMAIL
2.5两个静态同步方法
public class SychTest5 {
static class Phone{
synchronized static void sendEmail(){ //相当于synchronized(Phone.class)
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
synchronized static void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p.sendMsg();
}).start();
}
}
执行结果,持有的是同一个类锁
EMAIL
MSG
2.6 两个静态同步方法,两个对象p和p2去调用
public class SychTest6 {//?
static class Phone{
synchronized static void sendEmail(){
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
synchronized static void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
Phone p2 = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p2.sendMsg();
}).start();
}
}
执行结果,持有的是同一个类锁,和哪个对象去调用无关
EMAIL
MSG
2.7一个静态同步,一个非静态同步
public class SychTest7 {
static class Phone{
synchronized static void sendEmail(){
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
synchronized void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p.sendMsg();
}).start();
}
}
执行结果,一个持有类锁,一个持有对象锁
MSG
EMAIL
2.8一个静态同步方法,一个非静态同步方法,两个对象去调用
public class SychTest8 {
static class Phone{
synchronized static void sendEmail(){
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("EMAIL");
}
synchronized void sendMsg(){
System.out.println("MSG");
}
}
public static void main(String[] args) {
Phone p = new Phone();
Phone p2 = new Phone();
new Thread(()->{
p.sendEmail();
}).start();
try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
p2.sendMsg();
}).start();
}
}
执行结果,一个持有类锁,一个持有对象锁
MSG
EMAIL
3 同步代码示例
3.1传入class对象
public class SychTest13 {
public static synchronized void aa() {
System.out.println("aaa");
}
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
synchronized (SychTest13.class){
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("aaaaaaaaaaaa");
}
}).start();
new Thread(()->{
synchronized (SychTest13.class){
System.out.println("bbbbbbbbbb");
}
}).start();
}
}
执行结果
aaaaaaaaaaaa
bbbbbbbbbbb
3.2传入同一个对象t1
public class SychTest14 {
public static synchronized void aa() {
System.out.println("aaa");
}
public static void main(String[] args) throws InterruptedException {
SychTest13 t1 = new SychTest13();
new Thread(()->{
synchronized (t1){
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("aaaaaaaaaaaa");
}
}).start();
new Thread(()->{
synchronized (t1){
System.out.println("bbbbbbbbbb");
}
}).start();
}
}
执行结果
aaaaaaaaaaaa
bbbbbbbbbbb
3.3传入对象t1和t2
public class SychTest15 {
public static synchronized void aa() {
System.out.println("aaa");
}
public static void main(String[] args) throws InterruptedException {
SychTest13 t1 = new SychTest13();
SychTest13 t2 = new SychTest13();
new Thread(()->{
synchronized (t1){
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("aaaaaaaaaaaa");
}
}).start();
new Thread(()->{
synchronized (t2){
System.out.println("bbbbbbbbbb");
}
}).start();
}
}
执行结果
bbbbbbbbbb
aaaaaaaaaaaa
4 注意事项
4.1 异常
锁会被释放,其它线程可能进来继续执行。
会出现数据不一致的情况,多个servlet线程访问同一个资源时,如果第一个线程抛出异常,其他线程就会进入同步代码块,有可能会访问到异常产生时的数据(异常产生的数据很可能是不对的),出现数据不一致的情况
4.2 锁对象
synchronized (obj),obj不要用String、常量、基本数据类型。
因为使用字符串、常量,你可以使用,其它人也可以使用,你们很可能都不知道对方使用了,那么就有问题了