JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。
对于悟空老师的 对象锁和类锁
这种分类,在按老师的课程学习时便于理解。但学完后,自己还是应该明白。synchronized
就两种:
类.class
,普通方法锁的是this
使用相同对象为参数的代码都会同步执行
。类.class
。此时锁的是整个类对象。让同学们对Synchronized关键字有整理概念,从官网解释引出通俗解释,便于理解。从Synchronized关键字的地位说明该关键字的重要性。代码演示不用并发手段会带来的问题,吸引同学们带着疑问继续学习。分享IDEA的调试技巧,便于同学们实际操作。
同一时刻
最多只有一个
线程执行该段代码,以达到保证并发安全的效果。关键字
由 Java
原生支持。public class DisappearRequest1 implements Runnable {
static DisappearRequest1 instance = new DisappearRequest1();
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();// 让线程t1、t2顺序执行
t2.join();// 让线程t1、t2顺序执行
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
上面的代码我们预期的是输出200000
但三次执行结果如下,均少于预期:
135797
108613
106960
原因在于i++
看上去是一个操作,但实际上包含了三个动作:
i
i
加一i
的值写入到内存中这三个动作应该属于一个原子操作但上面的代码并没有。。。所以我们需要用synchronized
来实现这一点。
方法锁
(默认锁对象为this当前实例对象)和同步代码块锁
(自己指定锁对象)静态
的方法或指定锁为Class对象
public class SynchronizedObjectCodeBlock2 implements Runnable {
static SynchronizedObjectCodeBlock2 instance = new SynchronizedObjectCodeBlock2();
Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) {}
}
@Override
public void run() {
// synchronized (lock) {
System.out.println("我是对象锁的代码块形式。我叫 " + Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 运行结束。");
// }
}
}
输出结果,两个线程同时执行,这也就是前面 i++
出坑的原因。
我是对象锁的代码块形式。我叫 Thread-1
我是对象锁的代码块形式。我叫 Thread-0
Thread-1 运行结束。
Thread-0 运行结束。
接下来我们去掉代码中 synchronized
的注释。输出结果如下,两个线程按顺序执行。Bug解决了。
我是对象锁的代码块形式。我叫 Thread-0
Thread-0 运行结束。
我是对象锁的代码块形式。我叫 Thread-1
Thread-1 运行结束。
synchronized
的参数可以是任意对象实例
。如果我们只需要控制一把锁,那可以省掉lock
对象,直接使用this
。这也是大家常用的方式。
run()
内打断点,开启调试,然后按需要切换同线程放行。@Override
public void run() {
// 在这里面打断点
}
public class SynchronizedObjectMethod3 implements Runnable {
static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("飞泥洗的");
}
@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() + " 运行结束。");
}
}
概念(重要)
Java类可能有很多个实例对象,但只有1个Class对象形式1
:synchronized 加在static方法上。形式2
:synchronized(*.class) 代码块.本质
:所谓类锁,不过是 Class对象的锁而已。
用法效果
:类锁只能在同一时刻被一个对象拥有。
这里和前面不同的是,两个线程两个不同的实例。但是因为锁是加在类上的,所以对类的所有实例都有效。
public class SynchronizedClassStatic4 implements Runnable {
static SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4();
static SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("飞泥洗的");
}
@Override
public void run() {
method();
}
public static synchronized void method() {
System.out.println("我是类锁的第一种形式:修饰 static 方法。我叫: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 运行结束。");
}
}
按顺序输出,正常。
我是类锁的第一种形式:修饰 static 方法。我叫: Thread-0
Thread-0 运行结束。
我是类锁的第一种形式:修饰 static 方法。我叫: Thread-1
Thread-1 运行结束。
飞泥洗的
我们尝试去掉 static
变成 public synchronized void method() {...}
输出如下。
因为普通方法锁的是this
但现在两个线程是两个不同的实例,所以它们不相干了。
我是类锁的第一种形式:修饰 static 方法。我叫: Thread-1
我是类锁的第一种形式:修饰 static 方法。我叫: Thread-0
Thread-1 运行结束。
Thread-0 运行结束。
飞泥洗的
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(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 运行结束。");
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()) {
}
System.out.println("飞泥洗的");
}
}
输出如下,成功按顺序执行。
我是类锁的第二种形式: synchronized (*.class)。我叫: Thread-1
Thread-1 运行结束。
我是类锁的第二种形式: synchronized (*.class)。我叫: Thread-0
Thread-0 运行结束。
飞泥洗的
i++
。public class DisappearRequest1 implements Runnable {
static DisappearRequest1 instance = new DisappearRequest1();
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();// 让线程t1、t2顺序执行
t2.join();// 让线程t1、t2顺序执行
System.out.println(i);
}
@Override
public synchronized void run() {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
//重复代码略
@Override
public void run() {
synchronized (this) {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
public class DisappearRequest1 implements Runnable {
static DisappearRequest1 instance1= new DisappearRequest1();
static DisappearRequest1 instance2 = new DisappearRequest1();
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
t1.join();// 让线程t1、t2顺序执行
t2.join();// 让线程t1、t2顺序执行
System.out.println(i);
}
@Override
public void run() {
methon();
}
private static synchronized void methon(){
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
//重复代码略
private void methon() {
synchronized (DisappearRequest1.class) {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}