Java关键字synchronized

Java关键字synchronized

一、用途

synchronized可以实现线程同步,保证线程安全。被synchronized修饰的代码块或方法在同一时刻只允许一个线程进入临界区。

二、用法

  1. 修饰普通方法,即同步方法
  2. 修饰静态方法,即静态同步方法
  3. 修饰代码块,即同步代码块

三、例子

1、同步方法

代码中用synchronized修饰了method,启动线程t0和线程t1。我们的预期结果是某一线程进入临界区,另一个线程在等待。进入临界区的线程睡眠2s后退出临界区,另一个线程再进入临界区。

public class Sync {
    
    public synchronized void method() {
        System.out.println(Thread.currentThread().getName() + ": in");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out");
    }
    
    public static void main(String[] args) {
        final Sync s = new Sync();
        Thread t0 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method();
            }
        });
        t0.start();
        t1.start();
    }
    
}

运行结果:

1496348-20190826163958011-714855622.gif

可以看到是预期的结果。


现在只有一个方法,下面我们测试一下多个方法。

public class Sync {
    
    public synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + ": in method1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method1");
    }
    
    public void method2() {
        System.out.println(Thread.currentThread().getName() + ": in method2");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method2");
    }
    
    public static void main(String[] args) {
        final Sync s = new Sync();
        Thread t0 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method1();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method2();
            }
        });
        t0.start();
        t1.start();
    }
    
}

现在有两个方法分别是method1method2,method1是由synchronized修饰的,method2则是一个普通方法。

来看一下运行结果:

1496348-20190826164006797-1303595085.gif

似乎并不影响其他线程调用对象的非synchronized修饰的方法。


那么如果两个方法都是synchronized修饰的方法呢?

public class Sync {
    
    public synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + ": in method1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method1");
    }
    
    public synchronized void method2() {
        System.out.println(Thread.currentThread().getName() + ": in method2");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method2");
    }
    
    public static void main(String[] args) {
        final Sync s = new Sync();
        Thread t0 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method1();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method2();
            }
        });
        t0.start();
        t1.start();
    }
    
}

1496348-20190826164013448-1586796867.gif

看到运行结果我们会发现同一个对象的两个被synchronized修饰的不同方法不能同时被调用。


再试试不同对象

public class Sync {
    
    public synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + ": in method1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method1");
    }
    
    public synchronized void method2() {
        System.out.println(Thread.currentThread().getName() + ": in method2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method2");
    }
    
    public static void main(String[] args) {
        final Sync s0 = new Sync();
        final Sync s1 = new Sync();
        Thread t0 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s0.method1();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s1.method1();
            }
        });
        t0.start();
        t1.start();
    }
    
}

1496348-20190826164026653-973768629.gif

从运行结果可以看出调用不同对象的method1()方法是可以同时调用的。

那么不同对象的不同名字的方法依然是互不影响的。
1496348-20190826164034314-375307427.gif


那么是不是可以总结得到:synchronized修饰普通方法的时候是对对象进行加锁,即对象锁。

2、静态同步方法

把method1和methd2都改成静态方法,两个线程都去调用method1。

public class Sync {
    
    public static synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + ": in method1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method1");
    }
    
    public static synchronized void method2() {
        System.out.println(Thread.currentThread().getName() + ": in method2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method2");
    }
    
    public static void main(String[] args) {
        Thread t0 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                Sync.method1();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                Sync.method1();
            }
        });
        t0.start();
        t1.start();
    }
    
}

1496348-20190826164047866-688670215.gif

两个线程调用method1不能同时进入临界区,只有等到一个退出临界区后另一个才能进入。


再试试调用不同静态方法。

public class Sync {
    
    public static synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + ": in method1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method1");
    }
    
    public static synchronized void method2() {
        System.out.println(Thread.currentThread().getName() + ": in method2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": out method2");
    }
    
    public static void main(String[] args) {
        Thread t0 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                Sync.method1();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                Sync.method2();
            }
        });
        t0.start();
        t1.start();
    }
    
}

1496348-20190826164057566-1376706706.gif

依然是同样的结果,两个进程不能同时进入临界区。


synchronized修饰静态方法是是对类进行加锁,即类锁。

3、同步代码块

同步代码块的都是这样的:

synchronized (...) {
    ...
}
①对象锁
synchronized (this) {
    ...
}

像这样的写法是不能写在静态方法里的,效果与同步方法类似。

②类锁
synchronized (XXX.class) {
    ...
}

XXX指的是类名。

像这样的写法可以写在静态方法和普通方法中。①②两种写法是不能写成构造代码块的,但是能写在构造方法中。

③私有锁
Object lock
synchronized (lock) {
    ...
}

私有锁的粒度更小,不会与对象锁和类锁竞争。


代码演示一下:

public class Sync {
    
    public Integer a = new Integer(1);
    public void method() {
        System.out.println(Thread.currentThread().getName() + ": in method");
        synchronized (a) {
            System.out.println(Thread.currentThread().getName() + ": in synchronized block");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ": out synchronized block");
        }
        System.out.println(Thread.currentThread().getName() + ": out moetod");
    }
    
    public static void main(String[] args) {
        final Sync s = new Sync();
        
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                s.method();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                s.method();
            }
        });
        t0.start();
        t1.start();
    }
    
}

1496348-20190826164110569-661796351.gif

运行结果可以看到两个线程可以同时进入方法,但是不能同时进入synchronized (a){...}所包含的代码块。


synchronized (lock){...}中的lock是不同的对象时结果是不一样的。

举个例子:小明和小红两人可以同时走进房间,但是小明(或小红)进入房间后必须走出房间才能再次进入房间。

public class Sync {
    class Person {
        String name;
        Person(String name) {
            this.name = name;
        }
        void enterRoom() {
            System.out.println(Thread.currentThread().getName() + "-->" + name + ": 我准备好了!");
            synchronized (name) {
                System.out.println(Thread.currentThread().getName() + "-->" + name + ": 我进入房间了,并且我要在房间里待2秒!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                exitRoom();
            }
            
        }
        void exitRoom() {
            System.out.println(Thread.currentThread().getName() + "-->" + name + ": 我退出了房间!");
        }
    }
    public static void main(String[] args) {
        final Person p1 = new Sync().new Person("小明");
        final Person p2 = new Sync().new Person("小明");
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                p1.enterRoom();
            }
        });
        Thread t1 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                p2.enterRoom();
            }
        });
        t0.start();
        t1.start();
    }
    
}

1496348-20190826164118928-896562075.gif

姓名相同,即lock是同一个对象时,两个线程是不能同时进入的。


把p2改名叫小红

final Person p1 = new Sync().new Person("小明");
final Person p2 = new Sync().new Person("小红");

1496348-20190826164126104-905623767.gif

可以看到小明小红几乎同时进入房间,也就是这两个线程同步进行的。

转载于:https://www.cnblogs.com/Vaxy/p/11413430.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值