参考:Java高并发之魂:synchronized深度解析(_悟空_)
链接:https://pan.baidu.com/s/18P7U4mIUC9wUWiHhDWi2xg
提取码:bpij
代码:https://github.com/ouyangxizhu/concurrency_demo.git
synchronized是java的关键字,是java语言原生支持的,是最基本的互斥同步手段。
能够保证同一时刻最多只有一个线程执行该代码,以达到保证并发安全的效果。
两个线程同时执行a++,最后结果会比预计的少。(synchronized_demo->DisappearRequest1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 消失的请求
*/
public class DisappearRequest1 implements Runnable{
static DisappearRequest1 instance = new DisappearRequest1();
static int i = 0;
public static void main(String[] args){
try {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();//join方法保证该线程的方法执行完之后才能执行下面的代码。
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
for(int j = 0; j < 100000; j++)
i++;
}
}
因为i++这个操作不是原子操作。具体的解释如下:
i++其实是三个操作
注在本小结的阅读中,读者要是有问题或者想到其他形式(但是不知道运行结果),读者可以自己尝试或者文章后面会有全部形式的例子
默认锁对象为this,即当前实例对象。(synchronized_demo->SynchronizedObjectMethod2)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 对象锁实例,普通方法锁
*/
public class SynchronizedObjectMethod2 implements Runnable{
static SynchronizedObjectMethod2 instance = new SynchronizedObjectMethod2();
@Override
public void run() {
method();
}
public synchronized void method(){
System.out.println("我是对象锁的方法修饰符形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args){
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println("finished");
}
}
自己指定锁对象。(synchronized_demo->SynchronizedObjectCodeBlock3)
package synchronized_demo;
/**
Created by ouyangxizhu on 2019/5/15.
对象锁 代码块形式
*/
public class SynchronizedObjectCodeBlock3 implements Runnable{
static SynchronizedObjectCodeBlock3 instance = new SynchronizedObjectCodeBlock3();
@Override
public void run() {
synchronized (this) {
System.out.println(“我是对象锁的代码块形式,我是:” + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “运行完毕”);
}
}
public static void main(String[] args){
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println(“finished”);
}
}
运行结果
结果为顺序执行。
(synchronized_demo->SynchronizedClassStatic4)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 类锁 synchronized修饰静态方法形式
*/
public class SynchronizedClassStatic4 implements Runnable {
static SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4();
static SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4();
@Override
public void run() {
method();
}
public static synchronized void method(){
System.out.println("我是类锁的synchronized修饰静态方法形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
java类可以有很多对象,但是只有一个类(Class)对象,即synchronized(*.class)代码块形式。其实是Class对象的锁而已,只不过只有一个而已。(synchronized_demo->SynchronizedClassClass5)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 类锁 synchronized(*.class)形式
*/
public class SynchronizedClassClass5 implements Runnable {
static SynchronizedClassClass5 instance1 = new SynchronizedClassClass5();
static SynchronizedClassClass5 instance2 = new SynchronizedClassClass5();
@Override
public void run() {
method();
}
public synchronized void method(){
synchronized (SynchronizedClassClass5.class) {
System.out.println("我是对象锁的synchronized(*.class)形式,我是:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行完毕");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedStyle1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 两个线程同时访问一个对象的同步方法
*/
public class SynchronizedStyle1 implements Runnable{
static SynchronizedStyle1 instance = new SynchronizedStyle1();
@Override
public void run() {
method();
}
public synchronized void method(){
System.out.println("我是两个线程同时访问一个对象的同步方法形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args){
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedStyle2)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 两个线程访问两个对象的同步方法
*/
public class SynchronizedStyle2 implements Runnable{
static SynchronizedStyle2 instance1 = new SynchronizedStyle2();
static SynchronizedStyle2 instance2 = new SynchronizedStyle2();
@Override
public void run() {
synchronized (this) {
System.out.println("我是两个线程访问两个对象的同步方法形式,我是:" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行完毕");
}
}
public static void main(String[] args){
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive());//空循环
System.out.println("finished");
}
}
运行结果:
或者
注: 这两个结果表示,线程可以先运行Thread-0也可以先运行Thread-1。
(synchronized_demo->SynchronizedStyle3)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 两个线程访问synchronized的静态方法
*/
public class SynchronizedStyle3 implements Runnable {
static SynchronizedStyle3 instance1 = new SynchronizedStyle3();
static SynchronizedStyle3 instance2 = new SynchronizedStyle3();
@Override
public void run() {
method();
}
public static synchronized void method(){
System.out.println("我是两个线程访问synchronized的静态方法形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedStyle4)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 同时访问同步方法和非同步方法
*/
public class SynchronizedStyle4 implements Runnable {
static SynchronizedStyle4 instance = new SynchronizedStyle4();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public synchronized void method1(){
System.out.println("我是加锁形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public void method2(){
System.out.println("我是不加锁形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedStyle5)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 访问同一个对象的不同的普通同步方法
*/
public class SynchronizedStyle5 implements Runnable {
static SynchronizedStyle5 instance = new SynchronizedStyle5();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public synchronized void method1(){
System.out.println("我是加锁形式,是method1,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public synchronized void method2(){
System.out.println("我是加锁形式,是method2,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedStyle6)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 同时访问静态的synchronized方法和非静态的synchronized方法
*/
public class SynchronizedStyle6 implements Runnable {
static SynchronizedStyle6 instance = new SynchronizedStyle6();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public static synchronized void method1(){
System.out.println("我是静态加锁method1,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public synchronized void method2(){
System.out.println("我是非静态加锁method2,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedStyle7)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 方法抛出异常后会释放锁
* 一旦第一个线程抛出异常,第二个线程会立刻获得锁
*/
public class SynchronizedStyle7 implements Runnable {
static SynchronizedStyle7 instance = new SynchronizedStyle7();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0"))
method1();
else
method2();
}
public synchronized void method1(){
System.out.println("我是非静态加锁method1,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException();
}
public synchronized void method2(){
System.out.println("我是非静态加锁method2,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) ;//空循环
System.out.println("finished");
}
}
在一个同步方法中调用非同步方法时不是线程安全的(非同步方法不是线程安全,可以同时访问)。
同一线程的外层函数获得锁之后,内层函数可以直接获得该锁。
比如:假设一个类里面有两个同步方法,第一个方法里面有第二个同步方法的调用。如果一个实例对象的线程想执行第一个同步方法,当执行第一个方法里面的第二个方法的调用的时候会出现死锁。(执行第一个同步方法时获得了该实例对象的锁,当在该方法中调用第二个方法时,必须获得该实例对象锁,但是该锁已经被该实例对象持有。如果不可重入,会永远等待,产生死锁,第二个方法不会被执行)。
避免一次次解锁加锁,简化编程。
线程而非调用。
(synchronized_demo->SynchronizedRecursion1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 粒度证明 证明同一个方法是可重入的(递归调用本身)
*/
public class SynchronizedRecursion1 {
int a = 0;
public synchronized void method1() {
System.out.println("我是method1,a = " + a);
if (a == 0) {
a++;
method1();
}
}
public static void main(String[] args) {
SynchronizedRecursion1 synchronizedRecursion1 = new SynchronizedRecursion1();
synchronizedRecursion1.method1();
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedRecursion2)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 粒度证明 证明可重入不要求是同一个方法 调用类内另外的方法
*/
public class SynchronizedRecursion2 {
public synchronized void method1() {
System.out.println("我是method1" );
method2();
}
public synchronized void method2() {
System.out.println("我是method2" );
}
public static void main(String[] args) {
SynchronizedRecursion2 synchronizedRecursion1 = new SynchronizedRecursion2();
synchronizedRecursion1.method1();
System.out.println("finished");
}
}
(synchronized_demo->SynchronizedRecursion3)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 粒度证明 证明可重入不要求是同一个类中的 调用父类的方法
*/
public class SynchronizedRecursion3 {
public synchronized void doSomething() {
System.out.println("我是父类的方法" );
}
}
class TestClass extends SynchronizedRecursion3{
public synchronized void doSomething() {
System.out.println("我是子类的方法" );
super.doSomething();
}
public static void main(String[] args) {
TestClass s = new TestClass();
s.doSomething();
System.out.println("finished");
}
}
这个例子我觉得写的不好。自己写了一个
(synchronized_demo->SynchronizedRecursion3_1)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
*/
public class SynchronizedRecursion3_1 {
static TestSyn t = new TestSyn();
public static void method(){
synchronized(t.getClass()){
System.out.println("我是SynchronizedRecursion3_1的method方法");
}
}
}
class TestSyn{
public static void main(String[] args){
method();
}
public synchronized static void method(){
System.out.println("我是TestSyn的method方法");
SynchronizedRecursion3_1.method();
}
}
一旦这个锁已经被别的线程获得了,如果本线程还想获得,只能选择等待或者阻塞,直到别的线程释放这个锁。如果别的线程永远不释放,本线程永远拿不到。
Lock类有中断的能力,第一点,可以中断现在已经获得锁的线程的执行。第二点如果等待时间太长,可以退出。
当前线程要执行同步代码或者同步代码块时,当前线程必须获得指定对象锁,当前线程获得之后别的线程(想要执行该方法或者代码块)都要等待,直到当前线程执行完同步方法或者同步代码块,或者发生异常退出才会释放锁。
(synchronized_demo->SynchcronizedToLock)
package synchronized_demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by ouyangxizhu on 2019/5/15.
*/
public class SynchcronizedToLock {
Lock lock = new ReentrantLock();
public synchronized void method1(){
System.out.println("我是synchronized形式的锁");
}
public void method2(){
try {
lock.lock();
System.out.println("我是Lock形式的锁");
} finally {
lock.unlock();
}
}
public static void main(String[] args){
SynchcronizedToLock s = new SynchcronizedToLock();
s.method1();
s.method2();
}
}
(synchronized_demo->SynchronizedDecompilation)
package synchronized_demo;
/**
* Created by ouyangxizhu on 2019/5/15.
* 反编译字节码
*/
public class SynchronizedDecompilation {
private Object object = new Object();
public void insert(Thread thread){
synchronized (object){
}
}
}
我用的是windows系统,不同系统的指令不一样
1).monitor计数器为0,该线程会获得锁,之后计数器加一,之后的线程就没办法获得锁了。
2) 如果已经获得锁了,再一次进入同步代码块(可重入性质),计数器会递增。
3)monitor被其他线程获得,本线程阻塞,直到别的线程释放锁。
该指令会使计数器减一。计数为零时锁被释放。
每一个线程会复制主内存的副本(其实是个抽象的概念,真实是放在缓冲区当中的),当线程之间通讯时,是通过先写入主内存,再从主内存当中读出来实现的。
当执行synchronized的同步方法时,在执行之前会从主内存读,执行完毕之前(释放锁之前)会强制写入主内存。
1)锁的释放情况少
方法执行完毕后或者发生异常。(当长时间I/O等情况时,别的线程只能等待)
2)试图获得锁不能设定超时、不能中断一个试图获得锁的线程(Lock类可以)。
加锁和释放的时机单一,每个锁仅有单一的条件,可能是不够的,对比读写锁(其实读的时候不用加锁)
(synchronized_demo->LockExample)
package synchronized_demo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Created by ouyangxizhu on 2019/5/15.
*/
public class LockExample {
public static void main(String[] args){
ReentrantLock lock = new ReentrantLock();
lock.lock();//锁住
lock.unlock();//释放
boolean b1 = lock.tryLock();//可以知道是否可以获得锁
boolean b2 = lock.tryLock(3000, TimeUnit.SECONDS);//可以设置超时时间
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();//读写锁
reentrantReadWriteLock.readLock();//读锁
reentrantReadWriteLock.writeLock();//写锁
}
}
monitor存储在对象头当中,没有对象当然就没有monitor。
将大部分代码都放到synchronized中会使大部分代码串行工作,影响速度。
避免以下类型代码
synchronized (instance1) {
synchronized (instance2){
...
}
}
synchronized (instance2) {
synchronized (instance1){
...
}
}
注以上都是为了尽量少出错。使用更简单的,防止复杂。
主要看算法,不同版本的JVM不一样(比如等待时间最长的?刚进来的?随机的?)(公平锁 非公平锁)
这个需要看业务
1)synchronized尽量少的包裹代码块
2)可以使用读写锁(防止读的时候加锁)
可以自己实现接口
1)锁只能升级不能降级。
2)偏向锁,轻量级锁,重量级锁是为了优化synchronized关键字实现的,与对象头的标记有关。这里简单介绍一下:”
偏向锁主要解决不需要加锁的情况,即只有一个线程执行或者不竞争
轻量级锁解决少量竞争的情况(才用自旋锁)
JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。