七种情况
- 两个线程同时访问一个对象的同步方法
- 两个线程访问的是两个对象的同步方法
- 两个线程访问的是synchronized修饰的静态方法
- 同时访问同步方法与非同步方法
- 访问一个对象的不同的普通同步方法
- 同时访问静态synchronized方法和非静态的synchronized方法
- 方法抛出异常后,会释放锁
一 | 两个线程同时访问一个对象的同步方法
- 两个线程t1和t2
- 同一个对象,它们都是Runnabl的同一个实例instance
- 一个对象的同步方法,被synchronizd修饰的method
结论:串行
并发与串行其实是不同的概念,在单核CPU里面,所有指令都是串行的,并发指的在某一个时间段内,多个线程执行。与串行相对应的是并行,之后用串行和并行来区别。
public class SynchronizedObjectMethod3 implements Runnable {
static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
method();
}
public synchronized void method() {
System.out.println("I'm object method lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm object method lock " + Thread.currentThread().getName() + " is over");
}
}
二 | 两个线程访问的是两个对象的同步方法
- 两个线程t1、t2
- 两个对象instance1和instance2
尽管同步代码块对象锁是this,但这个this在起作用是两个不同的实例,用lock1也是一样的。
结论:并行
public class SynchronizedObjectCodeBlock2 implements Runnable {
static SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2();
static SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2();
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
synchronized (this) {
System.out.println("I'm object code block lock1 " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm object code block lock1 " + Thread.currentThread().getName() + " is over");
}
// synchronized (lock2) {
// System.out.println("I'm object code block lock2 " + Thread.currentThread().getName());
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println("I'm object code block lock2 " + Thread.currentThread().getName() + " is over");
// }
}
}
三 | 两个线程访问的是synchronized修饰的静态方法
public class SynchronizedStaticMethod4 implements Runnable {
static SynchronizedStaticMethod4 instance1 = new SynchronizedStaticMethod4();
static SynchronizedStaticMethod4 instance2 = new SynchronizedStaticMethod4();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
method();
}
public static synchronized void method() {
System.out.println("I'm object method lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm object method lock " + Thread.currentThread().getName() + " is over");
}
}
结论:串行
四 | 同时访问同步方法与非同步方法
结论当然是非同步方法不会受到影响
public class SynchronizedYesOrNo6 implements Runnable{
static SynchronizedYesOrNo6 instance = new SynchronizedYesOrNo6();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
System.out.println("I'm lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm lock " + Thread.currentThread().getName() + " is over");
}
public void method2() {
System.out.println("I'm not lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not lock " + Thread.currentThread().getName() + " is over");
}
}
试想一下如果本该作为临界资源的变量i,除了在同步代码块内,还在普通方法的地方被修改,会发生什么呢?
public class DisappearRequest1 implements Runnable {
static DisappearRequest1 instance = new DisappearRequest1();
static int i = 0;
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
for(int j=0; j<100000; j++) {
i++;
}
}
public void method2() {
for(int j=0; j<100000; j++) {
i++;
}
}
}
i的最后结果是小于200000的…所以临界资源必须进行互斥操作。
五 | 访问一个对象的不同的普通同步方法
public class SynchronizedDifferentMethod7 implements Runnable{
static SynchronizedDifferentMethod7 instance = new SynchronizedDifferentMethod7();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
System.out.println("I'm lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm lock " + Thread.currentThread().getName() + " is over");
}
public synchronized void method2() {
System.out.println("I'm not lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not lock " + Thread.currentThread().getName() + " is over");
}
}
结论串行:其实相当于是synchronized锁了同一个this对象
六 | 同时访问静态synchronized方法和非静态的synchronized方法
预测:并行
public class SynchronizedStaticAndNormal8 implements Runnable{
static SynchronizedStaticAndNormal8 instance = new SynchronizedStaticAndNormal8();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public static synchronized void method1() {
System.out.println("I'm static lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm static lock " + Thread.currentThread().getName() + " is over");
}
public synchronized void method2() {
System.out.println("I'm not static " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not static " + Thread.currentThread().getName() + " is over");
}
}
结论:并行
原因:static加锁的对象是该类的类类型,而普通方法锁加锁的对象是this,这显然是两把不同的锁。
章七 | 方法抛出异常后,会释放锁
要使用RuntimeException来抛出异常
public class SynchronizedException9 implements Runnable {
static SynchronizedException9 instance = new SynchronizedException9();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
// t1.join();
// t2.join();
while(t1.isAlive() || t2.isAlive()) {}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
public synchronized void method1() {
System.out.println("I'm static lock " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
// throw new Exception();
} catch (InterruptedException e) {
e.printStackTrace();
}
// catch (Exception e) {
// e.printStackTrace();
// }
throw new RuntimeException();
// System.out.println("I'm static lock " + Thread.currentThread().getName() + " is over");
}
public synchronized void method2() {
System.out.println("I'm not static " + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I'm not static " + Thread.currentThread().getName() + " is over");
}
}
总结
- 一把锁只能被一个线程所获取,其他线程只能等待
- 每个实例都对应有自己的一把锁,类锁就是所有实例的类类型的锁,有同一个类派生而来的实例自然只有一个类类型
- 无论发放还是正常执行完毕还是抛出异常,synchronized修饰的都将释放锁