从我们熟悉的同步锁了解多线程使用方式
synchronized几种使用方式
演示 (共享变量)同步锁的使用场景: 同步方法、 静态同步方法 、同步代码块
package com.allen.base.day02;
import java.util.Random;
public class SynchronizedDemo {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.OneClass();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.OneClass();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public synchronized void first() throws InterruptedException {
double random = Math.random();
System.out.println(random + " first======start=======>");
Thread.sleep(5000);
System.out.println(random + " first======end=======>");
}
public synchronized void second() throws InterruptedException {
System.out.println("second=====start========>");
Thread.sleep(5000);
System.out.println("second======end=======>");
}
public void one() throws InterruptedException {
double random = Math.random();
System.out.println("进入线程===========。。。。" + random);
synchronized (this) {
System.out.println(random + " one=======start======> ");
Thread.sleep(5000);
System.out.println(random + " one=======end======> ");
}
}
public synchronized void two() throws InterruptedException {
synchronized (this) {
System.out.println("two======start=======>");
Thread.sleep(5000);
System.out.println("two======end=======>");
}
}
public void OneClass() throws InterruptedException {
double random = Math.random();
System.out.println(random);
synchronized (SynchronizedDemo.class) {
System.out.println(random + " OneClass======start=======>");
Thread.sleep(5000);
System.out.println(random + " OneClass======end=======>");
}
}
}
几种测试场景:
1、非静态同步方法
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public synchronized void twoClass1() throws InterruptedException {
System.out.println("twoClass1======start=======>");
Thread.sleep(5000);
System.out.println("twoClass1======end=======>");
}
2、静态同步方法
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public synchronized static void twoClass2() throws InterruptedException {
System.out.println("twoClass2======start=======>");
Thread.sleep(5000);
System.out.println("twoClass2======end=======>");
}
3、同步代码块
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.one();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.one();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public void one() throws InterruptedException {
double random = Math.random();
System.out.println("进入线程===========。。。。" + random);
synchronized (this) {
System.out.println(random + " one=======start======> ");
Thread.sleep(5000);
System.out.println(random + " one=======end======> ");
}
}
组合场景:
1、静态同步方法与非静态同步方法
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public synchronized void twoClass1() throws InterruptedException {
System.out.println("twoClass1======start=======>");
Thread.sleep(5000);
System.out.println("twoClass1======end=======>");
}
public synchronized static void twoClass2() throws InterruptedException {
System.out.println("twoClass2======start=======>");
Thread.sleep(5000);
System.out.println("twoClass2======end=======>");
}
2、非静态同步方法与同步代码块
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.one();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public void one() throws InterruptedException {
double random = Math.random();
System.out.println("进入线程===========。。。。" + random);
synchronized (this) {
System.out.println(random + " one=======start======> ");
Thread.sleep(5000);
System.out.println(random + " one=======end======> ");
}
}
public synchronized void twoClass1() throws InterruptedException {
System.out.println("twoClass1======start=======>");
Thread.sleep(5000);
System.out.println("twoClass1======end=======>");
}
3、静态同步方法与同步代码块
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.one();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
demo.twoClass2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
threadA.start();
}
public void one() throws InterruptedException {
double random = Math.random();
System.out.println("进入线程===========。。。。" + random);
synchronized (this) {
System.out.println(random + " one=======start======> ");
Thread.sleep(5000);
System.out.println(random + " one=======end======> ");
}
}
public synchronized static void twoClass2() throws InterruptedException {
System.out.println("twoClass2======start=======>");
Thread.sleep(5000);
System.out.println("twoClass2======end=======>");
}
总结
Java中的每一个对象都可以作为锁,
具体表现为以下3种形式
对于普通同步方法,锁是当前实例对象
对于静态同步方法,锁是当前类的Class对象
对于同步代码块,锁是synchronized括号中配置的对象
如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取方法释放锁后才能获取锁
所有的静态同步方法用的也是同一把锁:类对象本身
非静态同步方法与静态同步方法对应的锁是两个不同对象,所以静态同步方法非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
不管是同一个实例对象的静态同步方法之间,还是不同实例对象的静态同步方法之间,只要他们是同一个类的实例对象 。
synchronized修饰方法使用的锁是当前对象this锁,
synchronized修饰静态方法使用的锁是当前类的字节码文件。
从JVM规范中可以看到synchronized在JVM中的实现原理,JVM基于进入与退出Monitor对象来实现方法的同步与代码块的同步,但两者实现细节不同,代码块同步是使用monitorenter和monitorexit指令实现的,而方法的同步是使用另一种方式实现的,细节在JVM规范中没有详细说明,但是,方法的同步同样可以使用这2个指令实现。
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每一个monitorenter必须对应一个monitorexit对应,任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态,线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即获取对象的锁。synchronized用的锁是在java对象的头里(Class字节码格式中的头部信息)