synchronized 介绍
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修饰类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
非线程安全
多个线程共同访问一个对象中的实例变量,才会出现“非线程安全”问题。
锁相关
- 多个对象对应多个锁。一个对象只有一个锁。
- 每次访问对修饰的方法,需要获取该对象的锁。所以即使调用的同一个对象不同的同步方法,每次也只能执行其中一个方法。
修饰方法
- 场景一:两个线程分为访问同一个实例变量的同步和非同步方法。
public class SynchronizedMethod {
private static class MyObject {
public synchronized void methodA() {
System.out.println("methodA start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA end");
}
public void methodB() {
System.out.println("methodB start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB end");
}
}
private static class MythreadA extends Thread {
private MyObject object;
MythreadA(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodA();
}
}
private static class MythreadB extends Thread {
private MyObject object;
MythreadB(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodB();
}
}
public static void main(String[] args) {
MyObject myObject = new MyObject();
MythreadA mythread1 = new MythreadA(myObject);
MythreadB mythread2 = new MythreadB(myObject);
mythread1.start();
mythread2.start();
}
}
输出结果:
methodA start
methodB start
methodA end
methodB end
methodB不需要获得锁,所以可以与同步方法异步调用。
- 场景二:两个线程分别访问同一个实例变量的不同的同步方法。
在上一个示例的methodB加上Syncrhoniezed
public class SynchronizedMethod {
private static class MyObject {
public synchronized void methodA() {
System.out.println("methodA start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA end");
}
public synchronized void methodB() {
System.out.println("methodB start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB end");
}
}
private static class MythreadA extends Thread {
private MyObject object;
MythreadA(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodA();
}
}
private static class MythreadB extends Thread {
private MyObject object;
MythreadB(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodB();
}
}
public static void main(String[] args) {
MyObject myObject = new MyObject();
MythreadA mythread1 = new MythreadA(myObject);
MythreadB mythread2 = new MythreadB(myObject);
mythread1.start();
mythread2.start();
}
}
输出结果:
methodA start
methodA end
methodB start
methodB end
他们是虽然是两个方法,但是因为是同一个实例(每次只能有一个线程获得对象的锁),所以也是顺序调用的。
修改代码块
当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
场景一: 两个线程分别访问同步方法和包含同步代码块的方法
public class SynchronizedBlock {
private static class MyObject {
public synchronized void methodA() {
System.out.println("methodA start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA end");
}
public void methodB() {
System.out.println("methodB start 1"); //这个不在同步方法内可以先执行
synchronized (this) {
System.out.println("methodB start 2");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB end");
}
}
}
private static class MythreadA extends Thread {
private MyObject object;
MythreadA(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodA();
}
}
private static class MythreadB extends Thread {
private MyObject object;
MythreadB(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodB();
}
}
public static void main(String[] args) {
MyObject myObject = new MyObject();
MythreadA mythread1 = new MythreadA(myObject);
MythreadB mythread2 = new MythreadB(myObject);
mythread1.start();
mythread2.start();
}
}
methodB方法
System.out.println("methodB start 1"); //这个不在同步方法内可以先执行
这块代码不在任何同步方法或者代码块中,可以不同等待直接运行。
- synchronized锁重入
即线程访问同步方法methodA获得锁后,统一可以访问另一个同步方法methodB - 出现异常时,会自动释放锁
- 同步具有继承性:重写父类同步方法时,子类方法若没有用synchronized修饰,也是一个非同步方法
Synchronized原理
了解Synchronized原理,对于上面的一些场景就一目了然了。
详细解释可以参考这里
每个对象有一个监视器锁monitor,每次进入Synchronized方法时,monitor相当于被占用处于锁定状态。monitor默认是0,线程进入后为1。
和static一起及Synchronize(class)
- 用在static方法上时,是对当前.javaw文件对哦应的Class类进行持锁(暂且称为类锁,前面实例变量锁暂称为对象锁)。
- 类锁和对象锁是不同的一种锁
实验实例:
public class SynchronizedStatic {
private static class MyObject {
public synchronized void methodA() {
System.out.println("methodA start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA end");
}
public static synchronized void methodB() {
System.out.println("methodB start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB end");
}
}
private static class MythreadA extends Thread {
private MyObject object;
MythreadA(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodA();
}
}
private static class MythreadB extends Thread {
private MyObject object;
MythreadB(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodB();
}
}
public static void main(String[] args) {
MyObject myObject = new MyObject();
MythreadA mythread1 = new MythreadA(myObject);
MythreadB mythread2 = new MythreadB(myObject);
mythread1.start();
mythread2.start();
}
}
结果:
methodA start
methodB start 2
methodA end
methodB end
- 类锁对所有实例对象有效
代码示例:
public class SynchronizedStatic2 {
private static class MyObject {
public static synchronized void methodA() {
System.out.println("methodA start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodA end");
}
public static synchronized void methodB() {
System.out.println("methodB start");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("methodB end");
}
}
private static class MythreadA extends Thread {
private MyObject object;
MythreadA(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodA();
}
}
private static class MythreadB extends Thread {
private MyObject object;
MythreadB(MyObject object) {
this.object = object;
}
@Override
public void run() {
object.methodB();
}
}
public static void main(String[] args) {
MyObject myObject = new MyObject();
MyObject myObject2 = new MyObject();//这里创建两个实例
MythreadA mythread1 = new MythreadA(myObject);
MythreadB mythread2 = new MythreadB(myObject2);
mythread1.start();
mythread2.start();
}
}
结果:
methodA start
methodA end
methodB start
methodB end
- 注意使用String的常量池特性。
String a="aa";
String b="aa";
//这里a和b持有的同一个对象锁
- 在static方法中,用Synchronized(class)作用和Synchronized static作用一样
参考
http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html
https://www.cnblogs.com/paddix/p/5367116.html
https://www.cnblogs.com/paddix/p/5367116.html