线程安全方式一: 同步代码块

  1. 同步代码块关键字: synchronized(同步锁) {}
  2. 将要同步的代码放在synchronized中
  3. 任意一个对象充当同步锁 Runnble接口中可以考虑使用tihs充当同步锁
  4. 多个线程必须使用统一个同步锁
  5. 缺点: 代码效率慢 代码内容只能有一个线程执行 相当于单线程 ```java /**

    • 创建人:LYY
    • 创建时间:2022/4/26
    • 同步代码块解决线程安全问题 */ public class SynchronizedTest {

      public static void main(String[] args) { TicketOne ticketOne = new TicketOne(); // 创建线程一 Thread t1 = new Thread(ticketOne); t1.setName(“线程一:”); Thread t2 = new Thread(ticketOne); t2.setName(“线程二:”); Thread t3 = new Thread(ticketOne); t3.setName(“线程三:”); t3.setPriority(10); t2.start(); t1.start(); t3.start(); }

}

class TicketOne implements Runnable{

  1. private int count = 100;
  2. Demo demo = new Demo();
  3. @Override
  4. public void run() {
  5. while (true) {
  6. // 同步代码块
  7. synchronized(demo){
  8. // 2. 将要执行的操作 卸载run方法中
  9. if (count>0){
  10. System.out.println(Thread.currentThread().getName()+ "count = "+count);
  11. } else if (count == 0){
  12. break;
  13. }
  14. try {
  15. Thread.sleep(100);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. count--;
  20. // 放弃当前线程权
  21. Thread.yield();
  22. }
  23. }
  24. }

}

  1. ```java
  2. /**
  3. * 创建人:LYY
  4. * 创建时间:2022/4/26
  5. * 模拟售票
  6. * 同100张车票 同时多个线程抢票
  7. * 每个线程执行一次 当前票数减一
  8. */
  9. // 1. 实现Runnable接口
  10. public class Ticket implements Runnable{
  11. private int count = 100;
  12. @Override
  13. public void run() {
  14. // 2. 将要执行的操作 卸载run方法中
  15. while (true) {
  16. // 同步锁
  17. synchronized(Ticket.class){
  18. if (count > 0) {
  19. System.out.println(Thread.currentThread().getName()+ "count = "+count);
  20. } else if (count == 0) {
  21. break;
  22. }
  23. count--;
  24. }
  25. }
  26. }
  27. }
  28. class TicketTest{
  29. @Test
  30. public void ticketTest() {
  31. // 3. 创建Runnable接口实现类的对象
  32. Ticket ticket = new Ticket();
  33. // 4. 将Runnable接口实现类的对象作为参数 传递给Thread对象 创建线程
  34. // 创建线程1
  35. Thread t1 = new Thread(ticket);
  36. t1.setName("线程一:");
  37. // 创建线程2
  38. Thread t2 = new Thread(ticket);
  39. t2.setName("线程二:");
  40. // 创建线程3
  41. Thread t3 = new Thread(ticket);
  42. t3.setName("线程三:");
  43. t3.start();
  44. t1.start();
  45. t2.start();
  46. }
  47. }

二、线程安全方式二:同步方法

  1. 创建一个方法 方法修饰符后添加synchronized关键字
  2. 将要执行(同步)的操作写在synchronized方法中
  3. run方法中调用synchronized方法
  4. synchronized方法依然存在同步锁 该对象为隐式的
  5. 继承Thread类中的同步方法需要声明为静态的 该同步方法同步锁隐式的 具体为class(.getClassz)
  6. 实现Runnable接口中的同步方法只需要添加synchronized 该方法的同步锁为隐式的 具体为this ```java /**

    • 创建人:LYY
    • 创建时间:2022/4/26
    • 使用同步方法解决继承Thread类的线程安全问题 */ public class ThreadTwo extends Thread{

      private static int count = 100; static boolean isOk = true;

      @Override public void run() { while (isOk) {

      1. show();

      } }

      /**

      • 同步方法 隐式的同步锁
      • 同步锁对象为class */ public static synchronized void show() {

        if (count > 0) {

        1. System.out.println(Thread.currentThread().getName() + count);
        2. count--;

        } else if (count == 0) {

        1. isOk = false;

        } } }

class ThreadTwoTest{ public static void main(String[] args) { ThreadTwo t1 = new ThreadTwo(); ThreadTwo t2 = new ThreadTwo(); ThreadTwo t3 = new ThreadTwo(); t3.setName(“线程三:”); t1.setName(“线程一:”); t2.setName(“线程二:”); t1.start(); t2.start(); t3.start(); } }

  1. ```java
  2. /**
  3. * 创建人:LYY
  4. * 创建时间:2022/4/26
  5. * 使用同步方法解决继承Thread类的线程安全问题
  6. */
  7. public class ThreadTwo extends Thread{
  8. private static int count = 100;
  9. static boolean isOk = true;
  10. @Override
  11. public void run() {
  12. while (isOk) {
  13. show();
  14. }
  15. }
  16. /**
  17. * 同步方法 隐式的同步锁
  18. * 同步锁对象为class
  19. */
  20. public static synchronized void show() {
  21. if (count > 0) {
  22. System.out.println(Thread.currentThread().getName() + count);
  23. count--;
  24. } else if (count == 0) {
  25. isOk = false;
  26. }
  27. }
  28. }
  29. class ThreadTwoTest{
  30. public static void main(String[] args) {
  31. ThreadTwo t1 = new ThreadTwo();
  32. ThreadTwo t2 = new ThreadTwo();
  33. ThreadTwo t3 = new ThreadTwo();
  34. t3.setName("线程三:");
  35. t1.setName("线程一:");
  36. t2.setName("线程二:");
  37. t1.start();
  38. t2.start();
  39. t3.start();
  40. }
  41. }

三、Lock锁解决线程安全问题

  1. 实现线程接口(Runnable)或类(Thread)
  2. 在线程类中创建Lock子类ReentrantLock对象
  3. 在需要同步数据前 手动使用ReentrantLock对象调用锁(lock)方法
  4. 同步数据操作接收 手动使用renntrantLock对象调用解锁(unlock)方法 ```java /**
    • 创建人:LYY
    • 创建时间:2022/4/27
    • 使用同步锁解决线程安全问题
    • 同步锁使用ReentrantLock类
      1. 在线程类中声明Lock的实现类对象 ReentrantLock
      1. 在需要同步的操作前 手动声明锁
      1. 在同步操作后 手动解锁 */ public class LockTest { }

class TicketTwo implements Runnable{

  1. private int count = 100;
  2. // 同步锁
  3. ReentrantLock reentrantLock = new ReentrantLock(true);
  4. @Override
  5. public void run() {
  6. while (true) {
  7. try {
  8. // 调用锁定方法
  9. reentrantLock.lock();
  10. if (count > 0) {
  11. System.out.println(Thread.currentThread().getName() + count);
  12. } else {
  13. break;
  14. }
  15. count--;
  16. } finally {
  17. // 调用解锁方法
  18. reentrantLock.unlock();
  19. }
  20. }
  21. }

}

  1. ```java
  2. /**
  3. * 创建人:LYY
  4. * 创建时间:2022/4/27
  5. */
  6. public class Client implements Runnable{
  7. private int count;
  8. /**
  9. * 同步锁对象
  10. */
  11. ReentrantLock lock = new ReentrantLock();
  12. @Override
  13. public void run() {
  14. for (int i = 0; i < 3; i++) {
  15. deposit();
  16. }
  17. }
  18. /**\
  19. * 存钱方法
  20. * 固定存款1000元
  21. */
  22. public void deposit() {
  23. try {
  24. // 同步锁锁定
  25. lock.lock();
  26. count +=1000;
  27. try {
  28. Thread.sleep(100);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println(Thread.currentThread().getName() + "存款成功! 余额:" + count);
  33. } finally {
  34. // 释放同步锁
  35. lock.unlock();
  36. }
  37. }
  38. }
  39. class ClientTest{
  40. public static void main(String[] args) {
  41. // 创建对象
  42. Client c = new Client();
  43. // 创建客户A(线程A)
  44. Thread thread1 = new Thread(c);
  45. Thread thread2 = new Thread(c);
  46. thread1.setName("线程A:");
  47. thread2.setName("线程B:");
  48. thread1.start();
  49. thread2.start();
  50. }
  51. }