java fun 关键字_Java关键字synchronized

Java关键字synchronized

一、用途

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

二、用法

修饰普通方法,即同步方法

修饰静态方法,即静态同步方法

修饰代码块,即同步代码块

三、例子

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();

}

}

运行结果:

7245c70b14089705b431ded0b621b38f.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();

}

}

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

来看一下运行结果:

35d6a0c49f48a4ea24a0366a28d22456.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();

}

}

b307a78b4f2504c9795758ccdd3dc8bc.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();

}

}

483b00d7f8f59fd31e15b1d29669655d.gif

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

那么不同对象的不同名字的方法依然是互不影响的。

ad5dc01e540a666e62e8118cac527ef4.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();

}

}

41e2688c65558c229cd7d0a471034d67.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();

}

}

daf118fbc45978e955df18cec9854950.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();

}

}

2f5ef3bdf0af2628638ef0f679815176.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();

}

}

d6f9b71983290390a359eea2e86c2636.gif

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

把p2改名叫小红

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

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

63cc76751a85b2ed7a377b8c29f76acd.gif

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值