1.synchronized简介:
1.1synchronized的作用:
官方解释:
Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors:if an objects is visible to more than one thread,all reads or writes to thar object’s variable are done through synchronized methods.
翻译:
Synchronized方法支持一种简单的策略,用于防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读或写操作都通过Synchronized方法完成。
总结一句话:能够保证在同一时刻最多只有一个线程执行该段代码,以保证并发安全的效果。
1.2synchronized的地位:
(1)synchronized是Java的关键字,被Java语言原生支持。
(2)是最基本的互斥手段。
1.3不用并发后果代码演示和原因分析
运行结果:
原因分析:我们发现结果和我们预期的结果不一样,为什么呢?主要原因是因为i++这个操作包含了三个动作,首先线程要去主内存中读取i的值,然后在自己的工作内存中把i+1,最后再把改变后的值写入主内存。在这个过程中线程有可能被中断,两个线程从主内存中读取的值有可能一样,然后往主内存中写值的过程中,后一个线程有可能会把前一个线程写的值覆盖。
2.synchronized的两个用法(对象锁和类锁):
2.1对象锁:包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)。
用this对象作为锁:
结果:
用两个不同的Object对象作为锁:
public class SynchronizedObjectCodeBlock2 implements Runnable {
static SynchronizedObjectCodeBlock2 soCodeBlock=new SynchronizedObjectCodeBlock2();
static Object obj01=new Object();
static Object obj02=new Object();
@Override
public void run() {
synchronized (obj01) {
System.out.println("我是lock1。我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "lock1部分运行结束");
}
synchronized (obj02) {
System.out.println("我是lock2。我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "lock2部分运行结束");
}
}
public static void main(String[] args) {
Thread thread01 = new Thread(soCodeBlock);
Thread thread02 = new Thread(soCodeBlock);
thread01.start();
thread02.start();
while (thread01.isAlive() || thread02.isAlive()){
}
System.out.println("finished");
}
}
结果:
用相同的object对象作为锁:
@Override
public void run() {
synchronized (obj01) {
System.out.println("我是lock1。我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "lock1部分运行结束");
}
synchronized (obj01) {
System.out.println("我是lock2。我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "lock2部分运行结束");
}
}
结果:
普通方法锁:
public class SynchronizedObjectMethod3 implements Runnable {
static SynchronizedObjectMethod3 soMethod=new SynchronizedObjectMethod3();
@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 thread01 = new Thread(soMethod);
Thread thread02 = new Thread(soMethod);
thread01.start();
thread02.start();
while (thread01.isAlive() || thread02.isAlive()){
}
System.out.println("finished");
}
}
结果:
2.2类锁:指synchronized修饰静态的方法或指定锁为Class对象。
静态方法锁:
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("我是类锁的第一种形式:static形式,我叫"+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 thread01 = new Thread(instance1);
Thread thread02 = new Thread(instance2);
thread01.start();
thread02.start();
while (thread01.isAlive() || thread02.isAlive()){
}
System.out.println("finished");
}
}
结果:
如果method()方法不加static,那么结果为:
结论:因为在这个例子中是两个runnable实例,如果访问没有加static修饰的方法,那么它们获得的锁是两把不同的对象锁,所以它们可以同时访问method方法,但是如果访问加static的方法,那么它们获得的锁是同一把锁,是类锁(所谓类锁就是Class对象的锁,java类可以有多个对象,但只有一个Class对象),所以它们不能同时访问。
类锁的第二种形式,synchronized(*.class)
public class SynchronizedClassClass5 implements Runnable{
static SynchronizedClassClass5 instance1 = new SynchronizedClassClass5();
static SynchronizedClassClass5 instance2 = new SynchronizedClassClass5();
@Override
public void run() {
method();
}
public 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 thread01 = new Thread(instance1);
Thread thread02 = new Thread(instance2);
thread01.start();
thread02.start();
while (thread01.isAlive() || thread02.isAlive()){
}
System.out.println("finished");
}
}
结果:
3.synchronized关键字的性质:
3.1可重入性
什么是可重入性:指的是同一线程的外层函数获得锁之后,内层函数可以直接获取改锁。
好处:避免死锁、提升封装性。
粒度:线程而非调用。
3.2不可中断行
什么是不可中断性:一旦这个锁被别人获取了,如果我还想获取,我只能选择等待或者阻塞,直到别的线程释放这个锁。如果别人永远不释放锁,那么我只能永远的等下去。
4.synchronized关键字的缺陷:
4.1效率低:锁的释放情况少、试图获取锁时不能设定超时时间、不能中断一个正在试图获取锁的线程。
4.2不够灵活(读写锁更加灵活):加锁和释放锁的时机单一,每个锁仅有单一的条件(每个对象),可能是不够的。
4.3无法知道是否成功获取到锁。