文章目录
前言
打完篮球好困,大家搞程序员的也不能总坐着啊,也要运动运动的 - -
提示:以下是本篇文章正文内容,下面案例可供参考
一、 synchronized的使用
对象锁
包括方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象)
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instance = new SynchronizedObjectLock();
@Override
public void run() {
// 同步代码块形式——锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
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();
}
}
输出结果为:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
两个线程使用的为一个实例,用的是同一把锁,所以线程t2必须等到线程t1释放之后才能获取锁
代码块形式:手动指定锁对象,可以使this也可以是自定义的锁
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instance = new SynchronizedObjectLock();
@Override
public void run() {
// 同步代码块形式——锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
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();
}
}
输出结果:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
方法锁形式:修饰普通方法,锁对象默认为this
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instance = new SynchronizedObjectLock();
@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();
}
}
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
修饰普通方法= 锁this,用的是同一个实例,所以要等待线程1 执行完毕才会执行线程2
类锁
直接锁class对象
所有线程需要的锁 都为一把。
修饰静态方法
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instance1 = new SynchronizedObjectLock();
static SynchronizedObjectLock instance2 = new SynchronizedObjectLock();
@Override
public void run() {
method();
}
// synchronized用在静态方法上,默认的锁就是当前所在的Class类,所以无论是哪个线程访问它,需要的锁都只有一把
public 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() + "结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
}
}
修饰静态方法时,锁的是class类,无论哪个实例线程,需要的锁都是同一把,需等待前一把释放才可获取
输出结果为:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
二、Synchronized原理分析
加锁和释放锁的原理
Monitorenter和Monitorexit指令,会让对象在执行,使其锁计数器加1或者减1。每一个对象在同一时间只与一个monitor(锁)相关联,而一个monitor在同一时间只能被一个线程获得,一个对象在尝试获得与这个对象相关联的Monitor锁的所有权的时候,monitorenter指令会发生如下3中情况之一:
- monitor计数器为0,意味着目前还没有被获得,那这个线程会立即获得,并将计数器加一。一旦加一,别的线程就别想获取,需要等待。
- 如果这个monitor已经拿到了锁的所有权,又重入了这把锁,那么就会继续+1,每重入一下,都会+1,会一直累加。
- 这把锁已经被别的线程获取了,等待释放。
monitorexit指令:释放对于monitor的所有权,释放过程很简单,就是讲monitor的计数器减1,如果减完以后,计数器不是0,则代表刚才是重入进来的,当前线程还继续持有这把锁的所有权,如果计数器变成0,则代表当前线程不再拥有该monitor的所有权,即释放锁。
可重入锁
指在外层方法中已经获取了对象锁,执行内部方法时会自动获取锁 ,不会因为之前已经获取过还没释放而阻塞(前提是锁对象是同一个对象或者class)
所以不难理解
- 执行monitorEnter获取锁:
重入则monitor计数器+1 - 执行monitorexit命令
monitor计数器-1,计数器=0 锁被释放。
总结
对于锁方法,静态方法,class对象 应分辨清楚