synchronized的两种用法
- 对象锁
包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定的锁对象) - 类锁
指synchronized修饰静态的方法或指定锁为class
- 在全局同步一个方法。
- 多个线程共享一个runnable时,可以使用对象锁。
- 多个线程间使用各自的runnable时,可以使用类锁。实现全局同步
synchronized工作流程
- 线程加锁时,将工作内存中的共享变量的值清空,从而使用共享变量时需要从主内存中重新读取最新的值。
- 线程解锁前,把共享变量的最新值刷新到主内存
synchronized对象锁之同步代码块锁
public class SynchronizedObjectCodeBlock implements Runnable {
static SynchronizedObjectCodeBlock instance = new SynchronizedObjectCodeBlock();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
//和thread.join()方法的原理类似
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("finish");
}
@Override
public void run() {
//如果有多个synchronized对象,则不能使用this了,使用自己定义的Object作为锁对象
synchronized (this) {
System.out.println("线程=" + Thread.currentThread().getName() + ",开始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程=" + Thread.currentThread().getName() + ",结束");
}
}
}
线程=Thread-0,开始
线程=Thread-0,结束
线程=Thread-1,开始
线程=Thread-1,结束
finish
- 如果有多个synchronized对象,则不能使用this了,使用自己定义的Object作为锁对象
public class SynchronizedObjectCodeBlock implements Runnable {
static SynchronizedObjectCodeBlock instance = new SynchronizedObjectCodeBlock();
Object lock1 = new Object();
Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
//和thread.join()方法的原理类似
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("finish");
}
@Override
public void run() {
//如果有多个synchronized对象,则不能使用this了,使用自己定义的Object作为锁对象
synchronized (lock1) {
System.out.println("线程=" + Thread.currentThread().getName() + ",锁lock1,开始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程=" + Thread.currentThread().getName() + ",锁lock1,结束");
}
synchronized (lock2) {
System.out.println("线程=" + Thread.currentThread().getName() + ",锁lock2,开始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程=" + Thread.currentThread().getName() + ",锁lock2,结束");
}
}
}
线程=Thread-0,锁lock1,开始
线程=Thread-0,锁lock1,结束
线程=Thread-1,锁lock1,开始
线程=Thread-0,锁lock2,开始
线程=Thread-1,锁lock1,结束
线程=Thread-0,锁lock2,结束
线程=Thread-1,锁lock2,开始
线程=Thread-1,锁lock2,结束
finish
synchronized对象锁之方法锁
- synchronized修饰普通方法,锁对象默认为this
public class SynchronizedObjectMethod implements Runnable {
static SynchronizedObjectCodeBlock instance = new SynchronizedObjectCodeBlock();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
//和thread.join()方法的原理类似
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("finish");
}
@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() + ",结束");
}
}
线程=Thread-0,锁lock1,开始
线程=Thread-0,锁lock1,结束
线程=Thread-1,锁lock1,开始
线程=Thread-1,锁lock1,结束
finish
synchronized类锁之static方法锁
- 如果不加static,是一个普通方法。可以并行
public class SynchronizedClassStatic implements Runnable{
static SynchronizedClassStatic instance1 = new SynchronizedClassStatic();
static SynchronizedClassStatic instance2 = new SynchronizedClassStatic();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
//和thread.join()方法的原理类似
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("finish");
}
@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() + ",结束");
}
}
线程=Thread-0,开始
线程=Thread-1,开始
线程=Thread-1,结束
线程=Thread-0,结束
finish
- 改为static方法后,一次只能有一个执行
等同于当前类的class锁
public class SynchronizedClassStatic implements Runnable {
static SynchronizedClassStatic instance1 = new SynchronizedClassStatic();
static SynchronizedClassStatic instance2 = new SynchronizedClassStatic();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
//和thread.join()方法的原理类似
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("finish");
}
@Override
public void run() {
method();
}
private synchronized static void method() {
System.out.println("线程=" + Thread.currentThread().getName() + ",开始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程=" + Thread.currentThread().getName() + ",结束");
}
}
线程=Thread-0,开始
线程=Thread-0,结束
线程=Thread-1,开始
线程=Thread-1,结束
finish
synchronized类锁之class锁
public class SynchronizedClassClass implements Runnable {
static SynchronizedClassClass instance1 = new SynchronizedClassClass();
static SynchronizedClassClass instance2 = new SynchronizedClassClass();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
//和thread.join()方法的原理类似
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("finish");
}
@Override
public void run() {
method();
}
private void method() {
synchronized (SynchronizedClassClass.class) {
System.out.println("线程=" + Thread.currentThread().getName() + ",开始");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程=" + Thread.currentThread().getName() + ",结束");
}
}
}
线程=Thread-0,开始
线程=Thread-0,结束
线程=Thread-1,开始
线程=Thread-1,结束
finish
synchronized与Lock.lock()/Lock.unlock()的区别
- 1.synchronized无法判断锁状态,不知当前线程是否锁住。Lock可以判断是否获取到锁
- 2.synchronized不可中断,如果线程迟迟拿不到一把锁,这把锁被其他线程占用,会出现该线程一直等待。
- 3.synchronized为非公平锁,任何新线程过来与原先排队等待的线程都有相同的机会获得锁,Lock可以自己写控制
- 4.关键字实现获取锁与释放锁,中间不可控
- 5.synchronized是托管于JVM上执行的。lock是java类在java代码上自己写的控制代码,所以synchronized相对于lock是比较低效的
- 6.由于性能问题,synchronized适合少量代码的同步问题,lock可用于大量代码的同步。
- 7.synchronized悲观锁,同一时刻只有一个线程可以访问,其他线程阻塞。
lock乐观锁,访问失败重试。
lock的作用是保护多线程访问的共享资源,而wait的作用是用于多线程之间的线程通信,作用不一样
class Outputter1 {
private Lock lock = new ReentrantLock();// 锁对象
public void output(String name) {
// TODO 线程输出方法
lock.lock();// 得到锁
try {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
lock.unlock();// 释放锁
}
}
}
这样就实现了和sychronized一样的同步效果,需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内。
Object.wait、Object.notify
- wait进入锁对象的等待队列
- notify,其他线程通过调用notify释放锁,则处于wait状态的线程,可以去竞争该锁。
sleep、wait
- sleep阻塞线程的执行,但是不改变锁的状态
- wait是需要锁来控制的。