Synchronized
1. synchronized的作用
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
2.不使用并发会有什么后果?
两个线程同时a++,最后结果会比预计的少
原因
i++,虽然是一行代码,但是实际上至少包含了以下这三个动作:
1.读取i的值
2.计算i+1
3.把i +1的计算结果写回到内存中,赋给i
3. synchronized的两种用法
3.1 对象锁
代码块形式:手动指定锁对象
方法锁形式:synchronized修饰普通方法,锁对象默认为this
3.1.1 对象锁的形式-同步代码块
/*
线程一进入第一个同步代码块
lock1线程一正在执行
lock1线程一运行结束
线程一退出第一个同步代码块
线程二进入第一个同步代码块
lock1线程二正在执行
线程一进入第二个同步代码块
lock2线程一正在执行
lock1线程二运行结束
线程二退出第一个同步代码块
lock2线程一运行结束
线程一退出第二个同步代码块
线程二进入第二个同步代码块
lock2线程二正在执行
lock2线程二运行结束
线程二退出第二个同步代码块
finished
线程一二在第一个同步等待,线程一出去之后,线程二进去,线程一同时也进去了第二个同步代码块,线程二退出了第一个同步代码块,
线程一退出了第二个同步代码块, 此时线程二进入第二个代码块,之后程序结束
*/
public class Synchronized implements Runnable {
private static Synchronized aSynchronized = new Synchronized();
Object lock1 = new Object();
Object lock2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
synchronized (lock1) {
System.out.println("lock1" + Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("lock1" + Thread.currentThread().getName() + "运行结束");
}
synchronized (lock2) {
System.out.println("lock2" + Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("lock2" + Thread.currentThread().getName() + "运行结束");
}
}
}
3.1.2 对象锁的形式-方法锁
/*
在方法加上锁
*/
public class Synchronized1 implements Runnable {
private static Synchronized1 aSynchronized = new Synchronized1();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
private synchronized void method() {
System.out.println(Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
3.2 类锁
Java类可能有很多个对象,但只有1个Class对象
形式1: synchronized加在static方法上
形式2:synchronized (*.class )代码块
只有一个Class对象:Java类可能会有很多个对象,但是只有1个Class对象。
本质:所以所谓的类锁,不过是Class对象的锁而已。
用法和效果:类锁只能在同一时刻被一个对象拥有。
3.2.1 synchronized加在static方法上
/*
加static ,为类锁,本质就是Synchronized2 一个对象
*/
public class Synchronized2 implements Runnable {
private static Synchronized2 aSynchronized1 = new Synchronized2();
private static Synchronized2 aSynchronized2 = new Synchronized2();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized1, "线程一");
Thread thread2 = new Thread(aSynchronized2, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
private static synchronized void method() {
System.out.println(Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
3.2.2 synchronized (*.class )代码块
/*
synchronized (*.class )代码块
*/
public class Synchronized3 implements Runnable {
private static Synchronized3 aSynchronized1 = new Synchronized3();
private static Synchronized3 aSynchronized2 = new Synchronized3();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized1, "线程一");
Thread thread2 = new Thread(aSynchronized2, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
synchronized (Synchronized3.class) {
System.out.println(Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
}
4. 调试技巧
可以看到线程一正在运行,线程二等待状态
打开调试窗查看
5. 多线程访问同步方法的7种情况
5.1 两个线程同时访问一个对象的同步方法
就一个Synchronized1对象可以使用sychronized保护
/*
在方法加上锁
*/
public class Synchronized1 implements Runnable {
private static Synchronized1 aSynchronized = new Synchronized1();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
private synchronized void method() {
System.out.println(Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
5.2 两个线程访问的是两个对象的同步方法
不能收到保护,并行执行
public class Synchronized1 implements Runnable {
private static Synchronized1 aSynchronized1 = new Synchronized1();
private static Synchronized1 aSynchronized2 = new Synchronized1();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized1, "线程一");
Thread thread2 = new Thread(aSynchronized2, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@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() + "运行结束");
}
}
}
5.3 两个线程访问的是synchronized的静态方法
升级到为类锁的情况,可以受到保护
/*
加static ,为类锁,本质就是Synchronized2 一个对象
*/
public class Synchronized2 implements Runnable {
private static Synchronized2 aSynchronized1 = new Synchronized2();
private static Synchronized2 aSynchronized2 = new Synchronized2();
Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized1, "线程一");
Thread thread2 = new Thread(aSynchronized2, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
private static synchronized void method() {
System.out.println(Thread.currentThread().getName() + "正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
5.4 同时访问同步方法与非同步方法
并行执行
/*
同时访问同步方法与非同步方法
*/
public class Synchronized4 implements Runnable {
private static Synchronized4 aSynchronized = new Synchronized4();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("线程一")){
method();
}else {
method1();
}
}
private synchronized void method() {
System.out.println(Thread.currentThread().getName() + "加锁正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁运行结束");
}
private void method1() {
System.out.println(Thread.currentThread().getName() + "无锁正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "无锁运行结束");
}
}
5.5 访问同一个对象的不同的普通同步方法
因为是同一个锁对象,所以要串行执行
/*
访问同一个对象的不同的普通同步方法
*/
public class Synchronized5 implements Runnable {
private static Synchronized5 aSynchronized = new Synchronized5();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("线程一")){
method();
}else {
method1();
}
}
private synchronized void method() {
System.out.println(Thread.currentThread().getName() + "加锁1正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁1运行结束");
}
private synchronized void method1() {
System.out.println(Thread.currentThread().getName() + "加锁2正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁2运行结束");
}
}
5.6 同时访问静态synchronized和非静态synchronized方法
因为一个是类锁,一个是对象锁,所以并行执行
/*
同时访问静态synchronized和非静态synchronized方法
*/
public class Synchronized6 implements Runnable {
private static Synchronized6 aSynchronized = new Synchronized6();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("线程一")){
method();
}else {
method1();
}
}
private static synchronized void method() {
System.out.println(Thread.currentThread().getName() + "加锁1正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁1运行结束");
}
private synchronized void method1() {
System.out.println(Thread.currentThread().getName() + "加锁2正在执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁2运行结束");
}
}
5.7 抛出异常,会释放锁
/*
抛出异常会释放锁
*/
public class Synchronized7 implements Runnable {
private static Synchronized7 aSynchronized = new Synchronized7();
public static void main(String[] args) {
Thread thread1 = new Thread(aSynchronized, "线程一");
Thread thread2 = new Thread(aSynchronized, "线程二");
thread1.start();
thread2.start();
while (thread1.isAlive() || thread2.isAlive()) {
}
System.out.println("finished");
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("线程一")){
method();
}else {
method1();
}
}
private synchronized void method() {
System.out.println(Thread.currentThread().getName() + "加锁1正在执行");
try {
Thread.sleep(1000);
throw new RuntimeException();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁1运行结束");
}
private synchronized void method1() {
System.out.println(Thread.currentThread().getName() + "加锁2正在执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "加锁2运行结束");
}
}
6. 性质
6.1 什么是可重入
指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁
好处:避免死锁、提升封装性
代码1
/*
递归调用本方法:输出:
a=0
a=1
说明是可重入的,因为进入一次线程又再次进入
*/
public class Reentrant {
int a = 0;
public synchronized void method() {
System.out.println("a=" + a);
if (a == 0) {
a++;
method();
}
}
public static void main(String[] args) {
Reentrant reentrant = new Reentrant();
reentrant.method();
}
}
代码2
/*
输出:method1
method2
因为可重入所以方法一去调用方法二可以执行
*/
public class Reentrant1 {
public synchronized void method1() {
System.out.println("method1");
method2();
}
public synchronized void method2() {
System.out.println("method2");
}
public static void main(String[] args) {
Reentrant1 reentrant = new Reentrant1();
reentrant.method1();
}
}
6.2 性质:不可中断
一旦这个锁已经被别人获得了,如果我还想获得,我只能选择等待或者阻塞,直到别的线程释放这个锁。如果别人永远不释放锁,那么我只能永远地等下去。
7. 原理
7.1 加锁和释放锁的原理
获取和释放锁的时机:进入和退出同步代码块(包括抛出异常)
/*
获取和释放锁的时机:进入和退出同步代码块(包括抛出异常)
*/
public class PrincipleOf {
ReentrantLock lock = new ReentrantLock();
public synchronized void method(){
System.out.println("synchronized");
}
//一定要try因为如果抛出异常,就会释放不了锁
public void method1(){
lock.lock();
try{
System.out.println("synchronized");
}finally {
lock.unlock();
}
}
}
字节码查看
进入目录:
执行命令:
javac Decompile.java
javap -verbose Decompile.class
/*
看字节码 同步代码块
*/
public class Decompile {
private Object object = new Object();
public void method(Thread thread){
synchronized (object){
}
}
}
有俩monitorexit,一个代表正常退出,一个代表异常退出。
7.2可重复原理:加锁次数计数器
JVM会记录被加锁的次数
第一次加锁时,次数从0变为1,之后如果再次加锁,就从1变成2以此类推
退出一层同步代码块时,计数减一,当计数为0的时候,代表锁释放
7.3 synchronized可以保证可见性
JMM内容
8. 缺陷
效率低:锁的释放情况少、试图获得锁时不能设定超时、不能中断一个正在试图获得锁的线程
不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的
无法知道是否成功获取锁
9. 面试题
使用注意点:锁的范围不宜过大、避免锁的嵌套
如何选择Lock和synchronized关键字?
多线程访问同步方法的各种具体情况
Synchronized使得同时只有一个线程可以执行,性能较差,有什么办法可以提升性能?
读写锁
我想更灵活地控制锁的获取和释放(现在释放锁的时机都被规定死了),怎么办?
使用lock锁
10. 总结
一句话介绍synchronized
JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。
Synchronized的作用、地位、不控制并发的后果
两种用法:对象锁和类锁
多线程访问同步方法的7种情况:是否是static, Synchronized方法等
Synchronized的性质:可重入、不可中断
原理:加解锁原理、可重入原理、可见性原理
标签:synchronized,Thread,System,笔记,学习,线程,println,new,out From: https://www.cnblogs.com/mrwyk/p/16889320.html