-
多线程同步机制
多线程编程中,一些敏感数据可能会被多个线程同时访问造成数据混乱(例如票数),使用线程同步机制,通过锁对象(对象实例或类实例)的方式来保证该段代码在任意时刻,最多只能由一个线程来访问,从而保证了数据的安全性。
-
synchronized 实现线程同步
1. 修饰普通方法
同步普通方法本质上锁的是:调用该方法的实例对象
(1)不加锁,同一个对象多个线程一起执行,互不干扰
public class Synchronized {public static void main(String[] args) {// 不加锁,同一个对象多个线程一起执行,互不干扰MyClass01 myClass01 = new MyClass01();new Thread(myClass01).start();new Thread(myClass01).start();}
}class MyClass01 implements Runnable {// 两个线程同时进入 run 方法@Overridepublic void run() {method01();method02();}public void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:(结果不固定)
(2)不加锁,不同对象多个线程一起执行,互不干扰
public class Synchronized {public static void main(String[] args) {// 不加锁,不同对象多个线程一起执行,互不干扰MyClass01 myClass0101 = new MyClass01();MyClass01 myClass0102 = new MyClass01();new Thread(myClass0101).start();new Thread(myClass0102).start();}
}class MyClass01 implements Runnable {// 两个线程同时进入 run 方法@Overridepublic void run() {method01();method02();}public void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:(结果不固定)
(3)给普通方法加锁,锁住了同一个对象实例 this,线程阻塞
public class Synchronized03 {public static void main(String[] args) {// 给普通方法加锁,锁住了同一个对象实例 this,线程阻塞MyClass02 myClass02 = new MyClass02();new Thread(myClass02).start();new Thread(myClass02).start();}
}class MyClass02 implements Runnable {// 两个线程同时执行到 run 方法@Overridepublic void run() {method01();method02();}// 这个方法在两个线程中都锁住了 this 对象// 所以必须等第一个线程执行完 method01 方法, 下一个线程才能执行public synchronized void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:
(4)给普通方法加锁,锁住了不同的对象实例,互不干扰
public class Synchronized03 {public static void main(String[] args) {// 给普通方法加锁,锁住不同的对象实例,MyClass02 myClass0201 = new MyClass02();MyClass02 myClass0202 = new MyClass02();new Thread(myClass0201).start();new Thread(myClass0202).start();}
}class MyClass02 implements Runnable {// 两个线程同时执行到 run 方法@Overridepublic void run() {method01();method02();}// 锁住两个不同的实例对象不会阻塞public synchronized void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:(结果不固定)
2. 修饰静态方法
同步静态方法本质上锁的是:调用该方法的类对象
(1)不加锁,同一个对象多个线程一起执行,互不干扰
public class Synchronized03 {public static void main(String[] args) {// 不加锁,同一个对象多个线程一起执行,互不干扰MyClass03 myClass03 = new MyClass03();new Thread(myClass03).start();new Thread(myClass03).start();}
}class MyClass03 extends Thread {// 两个线程同时执行到 run 方法@Overridepublic void run() {method01();method02();}public static void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public static void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:(结果不固定)
(2)不加锁,不同对象多个线程一起执行,互不干扰
public class Synchronized03 {public static void main(String[] args) {// 不加锁,不同对象多个线程一起执行,互不干扰MyClass03 myClass0301 = new MyClass03();MyClass03 myClass0302 = new MyClass03();new Thread(myClass0301).start();new Thread(myClass0302).start();}
}class MyClass03 extends Thread {// 两个线程同时执行到 run 方法@Overridepublic void run() {method01();method02();}public static void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public static void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:(结果不固定)
(3)给静态方法加锁,锁住了同一个对象实例,也就是同一个类实例,线程阻塞
public class Synchronized03 {public static void main(String[] args) {// 给静态方法加锁,锁住了同一个对象实例,也就是同一个类实例,线程阻塞MyClass04 myClass04 = new MyClass04();new Thread(myClass04).start();new Thread(myClass04).start();}
}class MyClass04 implements Runnable {// 两个线程同时执行到 run 方法@Overridepublic void run() {method01();method02();}// 锁住了同一个类实例,线程阻塞public static synchronized void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public static void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:
(4)给静态方法加锁,锁住了不同的对象实例,但本质锁的是同一个类实例,线程阻塞
public class Synchronized03 {public static void main(String[] args) {// 给静态方法加锁,锁住了同一个对象实例,也就是同一个类实例,线程阻塞MyClass04 myClass04 = new MyClass04();new Thread(myClass04).start();new Thread(myClass04).start();}
}class MyClass04 implements Runnable {// 两个线程同时执行到 run 方法@Overridepublic void run() {method01();method02();}// 锁住了同一个类实例,线程阻塞public static synchronized void method01() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("method01 方法被 " + Thread.currentThread().getName() + " 执行...");}public static void method02() {System.out.println("method02 方法被 " + Thread.currentThread().getName() + " 执行...");}
}
运行结果:
3. 修饰代码块
通过在 synchronized 关键字后面的对象(实例对象或类对象)来上锁,随便 new 个啥进去都行,只要保证不同线程中括号里的是同一个对象实例或类对象,那么就能成功上锁
(1)不加锁,同一个对象多个线程一起执行,互不干扰
public class Synchronized04 {public static void main(String[] args) {// 不加锁,同一个对象多个线程一起执行,互不干扰MyClass05 myClass05 = new MyClass05();for (int i = 0; i < 5; i++) {new Thread(myClass05).start();}}
}class MyClass05 implements Runnable {@Overridepublic void run() {try {Thread.sleep(1000);System.out.println("代码块被 " + Thread.currentThread().getName() + " 执行...");} catch (InterruptedException e) {e.printStackTrace();}}
}
运行结果:(结果不固定)
(2)加锁,但是每个线程传入的对象不一样,没锁住互不干扰
public class Synchronized04 {public static void main(String[] args) {// 加锁,但是每个线程传入的对象不一样,没锁住互不干扰for (int i = 0; i < 5; i++) {MyClass05 myClass05 = new MyClass05();new Thread(myClass05).start();}}
}class MyClass05 implements Runnable {@Overridepublic void run() {synchronized (this) {try {Thread.sleep(1000);System.out.println("代码块被 " + Thread.currentThread().getName() + " 执行...");} catch (InterruptedException e) {e.printStackTrace();}}}
}
运行结果:(结果不固定)
(3)加锁,每个线程传入同样的对象,线程阻塞
public class Synchronized04 {public static void main(String[] args) {// 加锁,每个线程传入同样的对象,线程阻塞MyClass05 myClass05 = new MyClass05();for (int i = 0; i < 5; i++) {new Thread(myClass05).start();}}
}class MyClass05 implements Runnable {@Overridepublic void run() {synchronized (this) {try {Thread.sleep(1000);System.out.println("代码块被 " + Thread.currentThread().getName() + " 执行...");} catch (InterruptedException e) {e.printStackTrace();}}}
}
运行结果:
(4)加锁,锁住类对象,线程阻塞
public class Synchronized04 {public static void main(String[] args) {// 加锁,锁住类对象,线程阻塞for (int i = 0; i < 5; i++) {MyClass05 myClass05 = new MyClass05();new Thread(myClass05).start();}}
}class MyClass05 implements Runnable {@Overridepublic void run() {synchronized (MyClass05.class) {try {Thread.sleep(1000);System.out.println("代码块被 " + Thread.currentThread().getName() + " 执行...");} catch (InterruptedException e) {e.printStackTrace();}}}
}
运行结果:
-
实现上锁步骤
1. 找到需要上锁的部分
2. 用代码块或者方法封装起来
3. 让多个线程判断锁的对象为同一个即可
-
线程死锁
多个线程都占用了对方的锁资源,即 A 线程想要 B 线程持有的锁资源,而 B 线程想要 A 线程持有的锁资源,且双方互不释放,就会导致死锁的发生。
public class DeadLock01 {public static void main(String[] args) {DeadLockDemo deadLockDemo01 = new DeadLockDemo(true);deadLockDemo01.setName("线程 A ");DeadLockDemo deadLockDemo02 = new DeadLockDemo(false);deadLockDemo02.setName("线程 B ");deadLockDemo01.start();deadLockDemo02.start();}
}class DeadLockDemo extends Thread {static Object object1 = new Object(); // 保证不管有多少个实例对象, 都共享同一个 object1static Object object2 = new Object();boolean flag;public DeadLockDemo(boolean flag) {this.flag = flag;}@Overridepublic void run() {// 线程 A 传进来的 flag 为 true, 线程 B 同步传进来的 flag 为 false// 这样就会出现一个问题, 那就是线程 A 持有 object1 锁并在尝试获取 object2 锁// 但是线程 B 持有着 object2 锁同时尝试获取 object1 锁, 双方都持有对方想要的锁却不释放// 这样就造成了程序死锁的发生if (flag) {synchronized (object1) { // 加入对象互斥锁System.out.println(Thread.currentThread().getName() + "进入 object1 锁的代码块");synchronized (object2) {System.out.println(Thread.currentThread().getName() + "进入 object2 锁的代码块");}}} else {synchronized (object2) {System.out.println(Thread.currentThread().getName() + "进入 object2 锁的代码块");synchronized (object1) {System.out.println(Thread.currentThread().getName() + "进入 object1 锁的代码块");}}}}
}
运行结果:
-
释放锁