目录
1 介绍synchronized
Synchronized是为了在一个资源(如一个方法就可以看出一个资源)上添加一个锁,保证在同一个时间点只有一个线程访问。
1.1 分类和作用范围
根据Synchronized作用范围,分为 类锁 和 实例对象锁 两种。
- 类锁:把一个类的静态方法当做一个资源,在同一个时刻只有一个线程可以访问这个资源,此时需要使用类锁。
- 实例锁:一个类实例化后的一个对象,把这个实例对象的一个方法当成一个资源,在同一个时刻只有一个线程可以访问这个资源,此时需要使用实例锁。
1.1.1 类锁
就是在静态方法的前面加上synchronized。
1 2 3 4 5 |
public class Test{ public synchronized static void print(){ ....; } } |
1.1.2 实例对象的锁
1. 非静态方法锁 synchronized method()
1 2 3 4 5 |
public class Test{ public synchronized void print(){ ....; } } |
某线程执行print()方法,则该对象将加锁。其它线程将无法执行该对象的所有synchronized块。上面的代码等同 synchronized(this)这种方式
1 2 3 4 5 6 7 |
public class Test{ public void print(){ synchronized(this){//锁住本对象,而不只是这个print方法。 ...; } } } |
2. 指定类实例锁 synchronized(Object ob){ }
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Test{ private String a = "test"; public void print(){ synchronized(a){//锁住a对象 ...; } } public synchronized void t(){ ...; //这个同步代码块不会因为print()而锁定. } } |
执行print(),会给对象a加锁,注意不是给Test的对象加锁,也就是说 Test对象的其它synchronized方法不会因为print()而被锁。同步代码块执行完,则释放对a的锁。
1.2 举例
问题:
1 2 3 4 5 6 |
public class Something(){ public synchronized void isSyncA(){} public synchronized void isSyncB(){} public static synchronized void cSyncA(){} public static synchronized void cSyncB(){} } |
那么,对于Something类的两个实例x与y,那么下列组方法何以被1个以上线程同时访问呢
- x.isSyncA()与x.isSyncB()
- x.isSyncA()与y.isSyncA()
- x.cSyncA()与y.cSyncB()
- x.isSyncA()与Something.cSyncA()
答案:
- 都是对同一个实例的synchronized域访问,因此不能被同时访问
- 是针对不同实例的,因此可以同时被访问
- 因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.isSyncA()与 Something.isSyncB()了,因此不能被同时访问。
- 能够同时访问,因为一个是实例锁,一个是类锁
2 Synchronized(object)解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
public class SycronizedTest { Entry entry; Node node; public Node getNode() { return node; } public void setNode(Node node) { this.node = node; } public Entry getEntry() { return entry; } public void setEntry(Entry entry) { this.entry = entry; } /** * 使用entry对象添加锁 * @param decription */ public void sysMethod1(String decription) { synchronized (entry) { try { System.out.println("method1:" + decription); Thread.sleep(1000000000); } catch (Exception e) { } } } /** * 使用node添加锁 * @param decription */ public void sysMethod2(String decription) { synchronized (node) { try { System.out.println("method2:" + decription); Thread.sleep(1000000000); } catch (Exception e) { } } } /** * 使用this添加锁 * @param decription */ public void sysMethod3(String decription) { synchronized (this) { try { System.out.println("method3:" + decription); Thread.sleep(1000000000); } catch (Exception e) { } } } public static void main(String[] args) { final SycronizedTest sycronizedTest = new SycronizedTest(); final Entry entry1 = new Entry(); final Node node = new Node(); sycronizedTest.setEntry(entry1); sycronizedTest.setNode(node); Thread thread1 = new Thread(new Runnable() { public void run() { sycronizedTest.sysMethod1("execute1 ...."); } }); Thread thread2 = new Thread(new Runnable() { public void run() { sycronizedTest.sysMethod1("excute2..."); } }); thread1.start(); thread2.start(); Thread thread3 = new Thread(new Runnable() { public void run() { sycronizedTest.sysMethod2("excute1..."); } }); Thread thread4 = new Thread(new Runnable() { public void run() { sycronizedTest.sysMethod2("excute2..."); } }); thread3.start(); thread4.start(); Thread thread5 = new Thread(new Runnable() { public void run() { sycronizedTest.sysMethod3("excute1..."); } }); Thread thread6 = new Thread(new Runnable() { public void run() { sycronizedTest.sysMethod3("excute2..."); } }); thread5.start(); thread6.start(); } } |
执行结果为
1 2 3 |
method1:execute1 .... method2:excute1... method3:excute1... |
3 wait和notify/notifyAll
何时释放锁? 两种情况:正常执行和wait–notify实现中途
3.1 Synchronized与wait/notify/notifyAll
总结1 调用wait()方法前的判断最好用while,而不用if;因为while可以实现被唤醒后线程再次作条件判断;而if则只能判断一次
总结2 有synchronized的地方不一定有wait,notify,但是有wait-notify的地方必须和Synchronized一块使用。即调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {…} 代码段内。
总结3 作用范围,synchronized的作用范围是一样,都是针对一个实例对象,而不是局限于某一个方法。
3.2 notify和notifyall理解
1. 相关类
(1)CountAddSub.java类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class CountAddSub { public synchronized void add1() { System.out.println("waitting add1 ..."); try { this.wait(); System.out.println("excute add1 "); } catch (Exception e) { System.out.println("异常"); } } public synchronized void add2() { System.out.println("waitting add2 ..."); try { this.wait(); System.out.println("excute add2 "); } catch (Exception e) { System.out.println("异常"); } } public synchronized void sub() { System.out.println("excute sub"); this.notify(); } } |
(2) Add1Runable类
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Add1Runnable implements Runnable { private CountAddSub countAddSub; public Add1Runnable(CountAddSub countAddSub){ this.countAddSub = countAddSub; } @Override public void run() { countAddSub.add1(); } } |
(3) Add2Runable类
1 2 3 4 5 6 7 8 9 10 11 |
public class Add2Runnable implements Runnable { private CountAddSub countAddSub; public Add2Runnable(CountAddSub countAddSub){ this.countAddSub = countAddSub; } @Override public void run() { countAddSub.add2(); } } |
(4) SubRunnable.java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class SubRunnable implements Runnable { private CountAddSub countAddSub; public SubRunbable(CountAddSub countAddSub){ this.countAddSub = countAddSub; } @Override public void run() { countAddSub.sub(); } } |
(5) ThreadTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class ThreadTest { public static void main(String[] args) { CountAddSub countAddSub = new CountAddSub(); Add1Runnable addRunnable1 = new Add1Runnable(countAddSub); Add2Runnable addRunnable2 = new Add2Runnable(countAddSub); SubRunbable subRunbable = new SubRunbable(countAddSub); System.out.println("执行完3个进程"); Thread t1 = new Thread(addRunnable2); t1.start(); Thread t2 = new Thread(addRunnable2); t2.start(); Thread t3 = new Thread(addRunnable1); t3.start(); Thread t4 = new Thread(addRunnable1); t4.start(); try { Thread.sleep(1000); } catch (Exception e) { System.out.println("exception"); } System.out.println("执行阻塞进程"); Thread subt = new Thread(subRunbable); subt.start(); } } |
执行结果为
1 2 3 4 5 6 7 8 |
执行完3个进程 waitting add2 ... waitting add2 ... waitting add1 ... waitting add1 ... 执行阻塞进程 excute sub excute add2 |
2、notifyAll()方法
修改CountAddSub中的sub方法
1 2 3 4 5 |
public synchronized void sub() { System.out.println("excute sub"); this.notifyAll(); } |
执行结果为
1 2 3 4 5 6 7 8 9 10 11 |
执行完3个进程 waitting add2 ... waitting add2 ... waitting add1 ... waitting add1 ... 执行阻塞进程 excute sub excute add2 excute add2 excute add1 excute add1 |
4 问题
synchronized锁只有两个粒度,一个是类锁,一个是实例锁,而且临界区互斥量为1,对于有些需要临界区大于1的,那么synchronized就不够用了。得需要专门的信号量等机制了。