1.线程安全问题的概述

image.png

2.线程安全问题的代码实现和分析

  1. package com.study_06.ThreadSafe;
  2. public class RunnableImpl implements Runnable {
  3. // 定义一个多个线程共享的票源
  4. private int ticket = 100;
  5. // 卖票
  6. @Override
  7. public void run() {
  8. // 使用死循环 让买票重复执行
  9. while (true) {
  10. // 先判断票是否存在
  11. if (ticket > 0) {
  12. // 提高安全问题出现的概率,让程序睡眠
  13. try {
  14. Thread.sleep(10);
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. //票存在 ticket---
  19. System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
  20. ticket--;
  21. }
  22. }
  23. }
  24. }
  1. package com.study_06.ThreadSafe;
  2. public class Demo01Ticket {
  3. public static void main(String[] args) {
  4. // 创建Runnable接口实现对象
  5. RunnableImpl run = new RunnableImpl();
  6. // 创建Thread类对象,构造方法中传递Runnable接口实现类对象
  7. Thread t0 = new Thread(run);
  8. Thread t1 = new Thread(run);
  9. Thread t2 = new Thread(run);
  10. // 调用start方法开启多线程
  11. t0.start();
  12. t1.start();
  13. t2.start();
  14. }
  15. }

image.png

3.线程同步

  1. 同步代码块
  2. 同步方法
  3. 锁机制

4.同步代码块

  • 同步代码块synchronized关键字可以用与方法中的某个区块中,表示只对这个区域的资源实行互斥访问

  • 格式:

    1. synchronized(同步锁){
    2. 需要同步操作的代码
    3. }

同步锁
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁

  1. 锁对象 可以是任意类型
  2. 多个线程对象 要使用同一把锁

    注意: 在任何时候吗,最多允许一个线程拥有同步锁,谁拿到锁谁就进入代码块中,其它的线程只能在外面等着(BLOCKED)

代码改进:

  1. package com.study_07.Synchronized;
  2. /*
  3. 1.通过代码块中的锁对象,可以使用任意的对象
  4. 2.但是必须保证多个线程使用的锁对象是同一个
  5. 3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
  6. */
  7. public class RunnableImpl implements Runnable {
  8. // 定义一个多个线程共享的票源
  9. private int ticket = 100;
  10. // 创建一个锁对象
  11. Object obj = new Object();
  12. // 卖票
  13. @Override
  14. public void run() {
  15. // 使用死循环 让买票重复执行
  16. while (true) {
  17. // 创建同步代码块
  18. synchronized (obj) {
  19. // 先判断票是否存在
  20. if (ticket > 0) {
  21. // 提高安全问题出现的概率,让程序睡眠
  22. try {
  23. Thread.sleep(10);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. //票存在 ticket---
  28. System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
  29. ticket--;
  30. }
  31. }
  32. }
  33. }
  34. }

5.同步技术的原理

image.png

6.同步方法

  • 同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程 执行该方法的时候,其它线程只能在方法外等着

  • 格式:

    1. public synchronized void method(){
    2. 可能会产生线程安全问题的代码
    3. }

    同步锁是谁? 对于非static方法,同步锁就是this 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)

  1. package com.study_08.Synchronized;
  2. public class RunnableImpl implements Runnable {
  3. // 定义一个多个线程共享的票源
  4. private int ticket = 100;
  5. // 卖票
  6. @Override
  7. public void run() {
  8. // 使用死循环 让买票重复执行
  9. System.out.println("this:"+this);
  10. while (true) {
  11. payTicket();
  12. }
  13. }
  14. /*
  15. 定义一个同步方法
  16. 同步方法也会把方法内部的代码锁住
  17. 只让一个线程执行
  18. 同步方法的对象是谁?
  19. 就是实现类对象
  20. */
  21. public synchronized void payTicket() {
  22. // 先判断票是否存在
  23. if (ticket > 0) {
  24. // 提高安全问题出现的概率,让程序睡眠
  25. try {
  26. Thread.sleep(10);
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. //票存在 ticket---
  31. System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
  32. ticket--;
  33. }
  34. }
  35. }

7.静态同步方法

  1. package com.study_08.Synchronized;
  2. public class RunnableImpl implements Runnable {
  3. // 定义一个多个线程共享的票源
  4. private static int ticket = 100;
  5. // 卖票
  6. @Override
  7. public void run() {
  8. // 使用死循环 让买票重复执行
  9. System.out.println("this:"+this);
  10. while (true) {
  11. payTicket();
  12. }
  13. }
  14. /*
  15. 静态同步方法
  16. 锁对象是谁?
  17. 不能是this
  18. this是创建对象之后产生的,静态方法优先于对象
  19. 静态的锁对象是本类的class属性-->class文件对象(反射)
  20. */
  21. public static /*synchronized*/ void payTicket() {
  22. // 先判断票是否存在
  23. synchronized (RunnableImpl.class) {
  24. if (ticket > 0) {
  25. // 提高安全问题出现的概率,让程序睡眠
  26. try {
  27. Thread.sleep(10);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. //票存在 ticket---
  32. System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
  33. ticket--;
  34. }
  35. }
  36. }
  37. }

8.Lock锁

  1. package com.study_09.Lock;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /*
  5. 使用Lock锁
  6. Lock实现提供了比使用synchronized方法和语句课获得的跟广泛的锁定操作
  7. Lock接口中的方法:
  8. void lock() 获取锁
  9. void unlock() 释放锁
  10. */
  11. public class RunnableImpl implements Runnable {
  12. // 定义一个多个线程共享的票源
  13. private int ticket = 100;
  14. // 1. 在成员位置创建一个ReentrantLock对象
  15. Lock l = new ReentrantLock();
  16. // 卖票
  17. @Override
  18. public void run() {
  19. // 使用死循环 让买票重复执行
  20. while (true) {
  21. l.lock();
  22. try {
  23. // 先判断票是否存在
  24. if (ticket > 0) {
  25. // 提高安全问题出现的概率,让程序睡眠
  26. try {
  27. Thread.sleep(10);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. //票存在 ticket---
  32. System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
  33. ticket--;
  34. }
  35. } finally {
  36. l.unlock(); // 无论程序是否异常,都会将锁对象释放
  37. }
  38. }
  39. }
  40. }