1 synchronized的原理
- 在 Java中每一个对象有且仅有一个同步锁,这也说明同步锁是依赖对象来存在的。
ex:synchronized(obj),obj这个对象就获取到了同步锁。 - 对同步锁的访问是互斥的
ex: 如果同时有两个线程A,B要获取同步锁,如果A获取到了锁,B要再次获取锁的话一定会失败,只有等到A释放了同步锁,线程B才可以获取到同步锁。
2 synchronized的基本规则
- 当一个线程访问某个对象的synchronized()方法或者synchronized()代码块的时候,其他线程再访问这个对象的同步方法或者同步代码块是阻塞的。
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class SynchronizedOne {
//当一个线程访问某个对象的同步代码块或者同步方法时其他对象对这个同步方法或者同步代码块的访问是阻塞的
public static void main(String[] args) {
Object o = new Object();
Sn sn = new Sn(o);
//分别启动三个线程
new Thread(sn).start();
new Thread(sn).start();
new Thread(sn).start();
}
static class Sn implements Runnable{
private Object object;
public Sn(Object object) {
this.object = object;
}
public void run() {
test1(object);
}
}
public static void test1(Object object) {
synchronized (object){
try {
System.out.println(Thread.currentThread().getName()+"....."+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"....."+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果是:
Thread-0.....1485602923861
Thread-0.....1485602925865
Thread-2.....1485602925865
Thread-2.....1485602927868
Thread-1.....1485602927868
Thread-1.....1485602929872
说明同一时间只有一个线程可以访问synchronized方法或代码块。
- 当一个线程对某个对象的synchronized方法或者代码块访问时,其他线程对这个对象的非synchronized的方法或者代码块的访问不是互斥的。
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class SynchronizedTwo {
public static void main(String[] args) {
NotBlock notBlock = new NotBlock();
new Thread(new MyThreadOne(notBlock)).start();
new Thread(new MyThreadTwo(notBlock)).start();
}
static class MyThreadOne implements Runnable {
NotBlock notBlock;
public MyThreadOne(NotBlock notBlock) {
this.notBlock = notBlock;
}
public void run() {
notBlock.test1();
}
}
static class MyThreadTwo implements Runnable {
NotBlock notBlock;
public MyThreadTwo(NotBlock notBlock) {
this.notBlock = notBlock;
}
public void run() {
notBlock.test2();
}
}
static class NotBlock {
public synchronized static void test1() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test2() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果是:
Thread-0...1485603824012
Thread-1...1485603824013
Thread-1...1485603827017
Thread-0...1485603829014
可以看到这两个方法几乎是同时访问,说明对于非同步的方法的访问不是互斥的。
- 当一个线程对某个对象的同步代码块或者方法访问时,对这个对象的其他的同步方法或者代码块的访问也是阻塞的。
在上面的例子test()加上synchronized看一下效果:
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class SynchronizedTwo {
public static void main(String[] args) {
NotBlock notBlock = new NotBlock();
new Thread(new MyThreadOne(notBlock)).start();
new Thread(new MyThreadTwo(notBlock)).start();
}
static class MyThreadOne implements Runnable {
NotBlock notBlock;
public MyThreadOne(NotBlock notBlock) {
this.notBlock = notBlock;
}
public void run() {
notBlock.test1();
}
}
static class MyThreadTwo implements Runnable {
NotBlock notBlock;
public MyThreadTwo(NotBlock notBlock) {
this.notBlock = notBlock;
}
public void run() {
notBlock.test2();
}
}
static class NotBlock {
public synchronized static void test1() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void test2() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果是:
Thread-0...1485603988023
Thread-0...1485603993027
Thread-1...1485603993027
Thread-1...1485603996029
说明对某个对象的多个同步方法或者同步代码块直接的访问都是阻塞的,这也说明synchronized是对象锁。
3 synchronized方法和代码块
synchronized是一个关键字,可以修饰方法,也可以修饰代码块。
ex:
synchronized方法示例
public synchronized void foo1() {
System.out.println("synchronized methoed");
}
synchronized代码块
public void foo2() {
synchronized (this) {
System.out.println("synchronized methoed");
}
}
在修饰代码块的时候可以看到比修饰方法控制的更加精细。
4 全局锁和实例锁
- 全局锁:全局锁控制的是整个类,无论有多少个对象。全局锁的用法是 static synchronized method 或者 synchronized(Class)
- 实例锁: 锁的是某一个对象实例,一般用法为:
synchronized method 或者 synchronized(this or obj)
看一下网上有一个很形象的例子:
pulbic class Something {
public synchronized void isSyncA(){}
public synchronized void isSyncB(){}
public static synchronized void cSyncA(){}
public static synchronized void cSyncB(){}}
假设,Something有两个实例x和y。分析下面4组表达式获取的锁的情况
(01) x.isSyncA()与x.isSyncB()
(02) x.isSyncA()与y.isSyncA()
(03) x.cSyncA()与y.cSyncB()
(04) x.isSyncA()与Something.cSyncA()
先列出公共需要访问的方法:
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class Test {
public synchronized static void test1() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void test2() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void test3() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void test4() {
try {
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "..." + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们来一种情况一种情况的分析:
- (01) x.isSyncA()与x.isSyncB()
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class ClassOrInstance {
public static void main(String[] args) {
//第一种情况test1.test3();test1.test4();
Test test1 = new Test();
Thread1 thread1 = new Thread1(test1);
new Thread(thread1).start();
new Thread(thread1).start();
}
static class Thread1 implements Runnable {
private Test test;
public Thread1(Test test) {
this.test = test;
}
public void run() {
test.test3();
}
}
static class Thread2 implements Runnable {
private Test test;
public Thread2(Test test) {
this.test = test;
}
public void run() {
test.test4();
}
}
}
访问时互斥的
结果是:
Thread-0...1485605611407
Thread-0...1485605614412
Thread-1...1485605614412
Thread-1...1485605617417
- (02) x.isSyncA()与y.isSyncA()
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class ClassOrInstance {
public static void main(String[] args) {
//第一种情况test1.test3();test1.test4();
// Test test1 = new Test();
// Thread1 thread1 = new Thread1(test1);
// new Thread(thread1).start();
// new Thread(thread1).start();
//第二种情况,test1.test3(), test2.test4()
Test test1 = new Test();
Test test2 = new Test();
Thread1 thread1 = new Thread1(test1);
Thread2 thread2 = new Thread2(test2);
new Thread(thread1).start();
new Thread(thread2).start();
}
static class Thread1 implements Runnable {
private Test test;
public Thread1(Test test) {
this.test = test;
}
public void run() {
test.test3();
}
}
static class Thread2 implements Runnable {
private Test test;
public Thread2(Test test) {
this.test = test;
}
public void run() {
test.test4();
}
}
}
是可以同时访问的
结果是:
Thread-1...1485605927744
Thread-0...1485605927744
Thread-0...1485605930745
Thread-1...1485605930745
- x.cSyncA()与y.cSyncB()
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class ClassOrInstance {
public static void main(String[] args) {
//第一种情况test1.test3();test1.test4();
// Test test1 = new Test();
// Thread1 thread1 = new Thread1(test1);
// new Thread(thread1).start();
// new Thread(thread1).start();
//第二种情况,test1.test3(), test2.test4()
// Test test1 = new Test();
// Test test2 = new Test();
// Thread1 thread1 = new Thread1(test1);
// Thread2 thread2 = new Thread2(test2);
// new Thread(thread1).start();
// new Thread(thread2).start();
//第三种情况 test1.test1(),test2.test2()
Test test1 = new Test();
Test test2 = new Test();
Thread1 thread1 = new Thread1(test1);
Thread2 thread2 = new Thread2(test2);
new Thread(thread1).start();
new Thread(thread2).start();
}
static class Thread1 implements Runnable {
private Test test;
public Thread1(Test test) {
this.test = test;
}
public void run() {
test.test1();
}
}
static class Thread2 implements Runnable {
private Test test;
public Thread2(Test test) {
this.test = test;
}
public void run() {
test.test2();
}
}
}
互斥访问
结果是:
Thread-0...1485606194772
Thread-0...1485606199776
Thread-1...1485606199776
Thread-1...1485606204777
- (04) x.isSyncA()与Something.cSyncA()
package com.yuxi;
/**
* Created by yuxi on 17/1/28.
*/
public class ClassOrInstance {
public static void main(String[] args) {
//第一种情况test1.test3();test1.test4();
// Test test1 = new Test();
// Thread1 thread1 = new Thread1(test1);
// new Thread(thread1).start();
// new Thread(thread1).start();
//第二种情况,test1.test3(), test2.test4()
// Test test1 = new Test();
// Test test2 = new Test();
// Thread1 thread1 = new Thread1(test1);
// Thread1 thread2 = new Thread1(test2);
// new Thread(thread1).start();
// new Thread(thread2).start();
//第三种情况 test1.test1(),test2.test2()
// Test test1 = new Test();
// Test test2 = new Test();
// Thread1 thread1 = new Thread1(test1);
// Thread1 thread2 = new Thread1(test2);
// new Thread(thread1).start();
// new Thread(thread2).start();
//第四种情况,test1.test3(),Test.test1()
Test test1 = new Test();
Thread1 thread1 = new Thread1(test1);
Thread2 thread2 = new Thread2();
new Thread(thread1).start();
new Thread(thread2).start();
}
static class Thread1 implements Runnable {
private Test test;
public Thread1(Test test) {
this.test = test;
}
public void run() {
test.test3();
}
}
static class Thread2 implements Runnable {
// private Test test;
//
// public Thread2(Test test) {
// this.test = test;
// }
public void run() {
Test.test2();
}
}
}
可以同时访问,因为一个是实例锁,一个是全局锁
结果是:
Thread-0...1485606527493
Thread-1...1485606527493
Thread-0...1485606530496
Thread-1...1485606531496