多线程之互斥锁(synchronized关键字)
来源:互联网 发布:加密锁软件下载 编辑:程序博客网 时间:2024/06/10 20:37
synchronized关键字经常被用来做线程互斥锁,但是使用不当的话,经常达不到目的。初学者常对锁住的是对象还是类有疑问。
原理:无论是对象还是类都有唯一的锁,synchronized只是声明了函数调用时需要什么锁,每个锁同一时间只能由一个线程获取,借此实现了线程互斥。
(1)分析对象锁
A.synchronized修饰非静态函数
接下来看实例:
public enum Person { Alice, Bob; public synchronized void say() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " say hello world! " +TimeUtil.getCurrentTime()); } public synchronized void speak() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime()); } public void print() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " print hello world! " + TimeUtil.getCurrentTime()); }}
这是一个Person类,其中有三个函数,say()和speak()这种声明方式意味着调用该函数需要持有Person实例的对象锁,即用哪个实例调用,则需要持有哪个对象的锁。print()函数无需任何锁。
public class ThreadA implements Runnable { @Override public void run() { Person.Alice.say(); }}public class ThreadB implements Runnable { @Override public void run() { Person.Bob.say(); }}public class ThreadC implements Runnable { @Override public void run() { Person.Alice.print(); }}public static String getCurrentTime() { SimpleDateFormat sdf = new SimpleDateFormat("mm:ss"); return sdf.format(new Date());}
创建多个线程,然后执行函数:
public class Main { public static void main(String[] args) { Thread threadA = new Thread(new ThreadA()); threadA.setName("threadA"); Thread threadB = new Thread(new ThreadB()); threadB.setName("threadB"); Thread threadC = new Thread(new ThreadC()); threadC.setName("threadC"); threadA.start(); System.out.println("A启动了 " + TimeUtil.getCurrentTime()); threadB.start(); System.out.println("B启动了 " + TimeUtil.getCurrentTime()); threadC.start(); System.out.println("C启动了 " + TimeUtil.getCurrentTime()); }}
输出结果为:
A启动了 20:26
B启动了 20:26
C启动了 20:26
threadA say hello world! 20:28
threadC print hello world! 24:21
threadB say hello world! 20:28
可以看出,用两个实例分别调用say()函数是不会出现互斥的。函数执行时,每个函数都可以拿到调用对象的锁。
接下来我们进行改动,将ThreadB改为:
public class ThreadB implements Runnable { @Override public void run() { Person.Alice.say(); }}
执行输出:
A启动了 25:33
B启动了 25:33
C启动了 25:33
threadA say hello world! 25:35
threadC print hello world! 25:35
threadB say hello world! 25:37
可以明显看出线程B和A存在对象锁竞争,A持有Alice锁的时候,B等待。
改动ThreadB为
public class ThreadB implements Runnable { @Override public void run() { Person.Alice.speak(); }}
执行输出:
A启动了 27:29
B启动了 27:29
C启动了 27:29
threadA say hello world! 27:31
threadC print hello world! 27:31
threadB speak hello world! 27:33
可以看出即使用Alice调用不同的函数,还是会出现等待.
总结:对象锁同一时间只能由一个线程持有,此时其余线程无法再用此对象调用需要此对象锁的函数。
B.synchronized修饰代码块
实例如下:
public enum Person { Alice, Bob; public synchronized void say() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " say hello world! " + TimeUtil.getCurrentTime()); } public void speak() { synchronized (Alice) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime()); } }}public class ThreadA implements Runnable { @Override public void run() { Person.Alice.say(); }}public class ThreadB implements Runnable { @Override public void run() { Person.Alice.speak(); }}public class Main { public static void main(String[] args) { Thread threadA = new Thread(new ThreadA()); threadA.setName("threadA"); Thread threadB = new Thread(new ThreadB()); threadB.setName("threadB"); threadA.start(); System.out.println("A启动了 " + TimeUtil.getCurrentTime()); threadB.start(); System.out.println("B启动了 " + TimeUtil.getCurrentTime()); }}
执行输出:
A启动了 35:26
B启动了 35:26
threadA say hello world! 35:28
threadB speak hello world! 35:30
可以看出A和B出现了互斥,A调用的say()函数用
synchronized关键字修饰,所以此时A占用Alice锁,speak中的代码块用synchronized (Alice)修饰,说明同样需要Alice锁,因此互斥。
改动speak函数:
public void speak() { synchronized (Bob) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime()); }}
将需求锁改为Bob,执行输出:
A启动了 38:36
B启动了 38:36
threadA say hello world! 38:38
threadB speak hello world! 38:38
可以看到互斥消失了。
继续更改:
public void speak() { synchronized (this) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime()); } }
这里的this指的是需要调用当前函数的对象锁,执行输出:
A启动了 40:45
B启动了 40:45
threadA say hello world! 40:47
threadB speak hello world! 40:49
互斥又出现了,因为又在竞争Alice锁
(2)分析类锁
每一个类都有唯一且同一时间只能被唯一线程持有的类锁。
实例如下:
public enum Person { Alice, Bob; public static synchronized void say() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " say hello world! " + TimeUtil.getCurrentTime()); } public static synchronized void speak() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime()); }}public class ThreadA implements Runnable { @Override public void run() { Person.say(); }}public class ThreadB implements Runnable { @Override public void run() { Person.speak(); }}public class Main { public static void main(String[] args) { Thread threadA = new Thread(new ThreadA()); threadA.setName("threadA"); Thread threadB = new Thread(new ThreadB()); threadB.setName("threadB"); threadA.start(); System.out.println("A启动了 " + TimeUtil.getCurrentTime()); threadB.start(); System.out.println("B启动了 " + TimeUtil.getCurrentTime()); }}
执行输出:
A启动了 44:18
B启动了 44:18
threadA say hello world! 44:20
threadB speak hello world! 44:22
明显看到线程互斥。
改动代码:
public static void speak() { synchronized (Person.class) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " speak hello world! " + TimeUtil.getCurrentTime()); } }
改动speak函数,将里面的的代码用 synchronized (Person.class) 修饰,执行输出:
A启动了 44:18
B启动了 44:18
threadA say hello world! 44:20
threadB speak hello world! 44:22
两个线程依旧互斥,因为还在竞争类锁。
总结:类锁类似对象锁,唯一且同一时间只能由唯一线程持有。
最后补充一点:类锁和对象锁是两套互斥机制,互不影响,具体看你的函数需求的是对象锁(哪个对象)还是类锁
- 多线程之互斥锁(synchronized关键字)
- 多线程之synchronized关键字详解
- Java多线程之synchronized关键字
- JAVA多线程之Synchronized关键字
- 多线程(二):synchronized 关键字
- 多线程(三) synchronized关键字
- 浅析java多线程之Synchronized关键字
- java多线程之synchronized和volatile关键字
- Java 多线程之synchronized关键字详解
- Java 多线程之synchronized关键字详解
- Java多线程之synchronized关键字详解
- Java 多线程之synchronized关键字详解
- Java 多线程并发编程之 Synchronized 关键字
- Java 多线程并发编程之 Synchronized 关键字
- Java多线程基础--04之 synchronized关键字
- Java 多线程并发编程之 Synchronized 关键字
- Java 多线程(六) synchronized关键字详解
- Java 多线程(六) synchronized关键字详解
- Minor GC和Full GC触发条件总结
- C++虚函数的实现机制
- MySQL Innodb引擎修改库名(rename schema name)
- ognl 取得request、session 的值
- Final关键字
- 多线程之互斥锁(synchronized关键字)
- Codeforces 276C Little Girl and Maximum Sum【贪心】
- 【Linux】Linux环境下Oracle SqlPlus中方向键问题的解决方法
- 23种设计模式之代理模式
- SSL证书实现https,环境:phpStudy下Apache环境
- JQGird中几点
- 深入理解Java:SimpleDateFormat安全的时间格式化
- [机器学习]符号表
- Meaning of logging information (I,O,R,W,U,E)