您的位置:首页 > 文旅 > 旅游 > Java重修笔记 第五十六天 坦克大战(六)多线程基础 - 线程同步、死锁

Java重修笔记 第五十六天 坦克大战(六)多线程基础 - 线程同步、死锁

2024/10/5 21:14:08 来源:https://blog.csdn.net/Code___slave/article/details/142237764  浏览:    关键词:Java重修笔记 第五十六天 坦克大战(六)多线程基础 - 线程同步、死锁
  • 多线程同步机制

        多线程编程中,一些敏感数据可能会被多个线程同时访问造成数据混乱(例如票数),使用线程同步机制,通过锁对象(对象实例或类实例)的方式来保证该段代码在任意时刻,最多只能由一个线程来访问,从而保证了数据的安全性。

  • 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 锁的代码块");}}}}
}

运行结果:

  • 释放锁
1. 什么操作会释放锁资源
        (1)当这个同步代码块执行结束后,自动释放锁资源
        (2)遇到 break 或者 return 语句强制退出方法/代码块时,自动释放锁资源
        (3)同步代码块或同步方法中遇到未处理的 Error 或者 Exception 异常退出时,自动释放锁资源
        (4)通过 wait 方法来让当前线程暂停(join 方法底层调用 wait 方法),线程暂停后会释放锁资源
2. 什么操作不会释放锁资源
        (1)调用 sleep 、yield 方法时,不会释放锁资源
        (2)调用 suspend 方法将线程挂起,也不会释放锁资源

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com