• 线程安全,与多线程的应用


    一、线程安全

    1.什么是线程安全

    2.用程序模拟线程安全问题

    1. public class Account {
    2. private double money;//余额
    3. private String cardId;//卡号
    4. public Account() {
    5. }
    6. public Account(double money, String cardId) {
    7. this.money = money;
    8. this.cardId = cardId;
    9. }
    10. public void drawMoney(double money){
    11. //先搞清楚是谁来取钱
    12. String name = Thread.currentThread ().getName ();
    13. //1.判断账户金额是否足够
    14. if (this.money>=money){
    15. System.out.println (name+"开始取钱");
    16. this.money-=money;
    17. System.out.println (name+"取钱成功后余额为"+this.money);
    18. }else {
    19. System.out.println ("未取钱余额不足");
    20. }
    21. }
    22. public double getMoney() {
    23. return money;
    24. }
    25. public void setMoney(double money) {
    26. this.money = money;
    27. }
    28. public String getCardId() {
    29. return cardId;
    30. }
    31. public void setCardId(String cardId) {
    32. this.cardId = cardId;
    33. }
    34. }
    35. public class MyThread extends Thread{
    36. private Account acc;
    37. public MyThread(Account acc,String name){
    38. super ( name );
    39. this.acc=acc;
    40. }
    41. @Override
    42. public void run() {
    43. // if (acc.getMoney ()>=100000.0){
    44. // System.out.println (Thread.currentThread ().getName ()+"开始取钱");
    45. // acc.setMoney (acc.getMoney ()-100000.0);
    46. // System.out.println (Thread.currentThread ().getName ()+"取钱成功");
    47. // System.out.println ( Thread.currentThread ().getName ()+"取钱后账户余额"+acc.getMoney () );
    48. // }else {
    49. // System.out.println ("账户余额不足");
    50. // }
    51. acc.drawMoney ( 100000.0 );
    52. }
    53. }
    54. public class ThreadTest {
    55. public static void main(String[] args) {
    56. //1.创建一个账户类对象代表两个人的共享账户
    57. Account acc = new Account ( 100000.0, "787878787877" );
    58. //2.创建两个线程,分别表示小明,小红,两人同时去一个账户中取钱10w
    59. Thread m1 = new MyThread ( acc,"小明" );
    60. Thread m2 = new MyThread ( acc,"小红" );
    61. m1.start ();
    62. m2.start ();
    63. }
    64. }

     3.如何解决线程安全问题?

    1.线程同步

    解决方案:加锁

    2.如何实现线程同步(加锁)

    1.同步代码块

    1. public class Account {
    2. private double money;//余额
    3. private String cardId;//卡号
    4. public Account() {
    5. }
    6. public Account(double money, String cardId) {
    7. this.money = money;
    8. this.cardId = cardId;
    9. }
    10. public void drawMoney(double money){
    11. //先搞清楚是谁来取钱
    12. String name = Thread.currentThread ().getName ();
    13. //1.判断账户金额是否足够
    14. synchronized (this) {//同步代码块上锁 推荐用共享资源作为锁(this)
    15. if (this.money>=money){
    16. System.out.println (name+"开始取钱");
    17. this.money-=money;
    18. System.out.println (name+"取钱成功后余额为"+this.money);
    19. }else {
    20. System.out.println (name+"未取钱余额不足");
    21. }
    22. }
    23. }
    24. public static void test(){ //静态方法推荐用类名上锁
    25. synchronized (Account.class){
    26. }
    27. }
    28. public double getMoney() {
    29. return money;
    30. }
    31. public void setMoney(double money) {
    32. this.money = money;
    33. }
    34. public String getCardId() {
    35. return cardId;
    36. }
    37. public void setCardId(String cardId) {
    38. this.cardId = cardId;
    39. }
    40. }
    2.同步方法

    1. public synchronized void drawMoney(double money){
    2. //先搞清楚是谁来取钱
    3. String name = Thread.currentThread ().getName ();
    4. //1.判断账户金额是否足够
    5. if (this.money>=money){
    6. System.out.println (name+"开始取钱");
    7. this.money-=money;
    8. System.out.println (name+"取钱成功后余额为"+this.money);
    9. }else {
    10. System.out.println (name+"未取钱余额不足");
    11. }
    12. }

    3.Lock锁

    1. import java.util.concurrent.locks.Lock;
    2. import java.util.concurrent.locks.ReentrantLock;
    3. public class Account {
    4. private double money;//余额
    5. private String cardId;//卡号
    6. //创建一个锁对象
    7. private final Lock lk=new ReentrantLock ();//final 保护锁对象
    8. public Account() {
    9. }
    10. public Account(double money, String cardId) {
    11. this.money = money;
    12. this.cardId = cardId;
    13. }
    14. public void drawMoney(double money){
    15. //先搞清楚是谁来取钱
    16. String name = Thread.currentThread ().getName ();
    17. //1.判断账户金额是否足够
    18. lk.lock ();//加锁
    19. try {
    20. if (this.money>=money){
    21. System.out.println (name+"开始取钱");
    22. this.money-=money;
    23. System.out.println (name+"取钱成功后余额为"+this.money);
    24. }else {
    25. System.out.println (name+"未取钱余额不足");
    26. }
    27. } catch (Exception e) {
    28. e.printStackTrace ();
    29. } finally {
    30. lk.unlock ();//解锁
    31. }
    32. }
    33. public static void test(){ //静态方法推荐用类名上锁
    34. synchronized (Account.class){
    35. }
    36. }
    37. public double getMoney() {
    38. return money;
    39. }
    40. public void setMoney(double money) {
    41. this.money = money;
    42. }
    43. public String getCardId() {
    44. return cardId;
    45. }
    46. public void setCardId(String cardId) {
    47. this.cardId = cardId;
    48. }
    49. }

    二、线程通信(前提保证线程安全)

    1.什么是线程通信

    2.代码实例

    1. public class ThreadTest {
    2. public static void main(String[] args) {
    3. Desk desk=new Desk ();
    4. //创建3个生产者线程
    5. new Thread ( ()-> {
    6. while (true) {
    7. desk.put();
    8. }
    9. },"厨师1" ).start ();
    10. new Thread ( ()-> {
    11. while (true) {
    12. desk.put();
    13. }
    14. },"厨师2" ).start ();
    15. new Thread ( ()-> {
    16. while (true) {
    17. desk.put();
    18. }
    19. },"厨师3" ).start ();
    20. //创建两个消费者线程
    21. new Thread(()->{
    22. while (true) {
    23. desk.get();
    24. }
    25. },"吃货1").start ();
    26. new Thread(()->{
    27. while (true) {
    28. desk.get();
    29. }
    30. },"吃货2").start ();
    31. }
    32. }
    33. import java.util.ArrayList;
    34. import java.util.List;
    35. public class Desk {
    36. private List list =new ArrayList<> ();
    37. //放包子
    38. //厨师1,厨师2,厨师3
    39. public synchronized void put() {
    40. try {
    41. String name = Thread.currentThread ().getName ();
    42. //判断是否由包子
    43. if(list.size ()==0){
    44. list.add ( name+"做的肉包子" );
    45. System.out.println (name+"做了个肉包子");
    46. Thread.sleep ( 2000 );
    47. //唤醒别人,等待自己,(用锁对象调用)
    48. this.notifyAll ();//先唤醒 后等待
    49. this.wait ();
    50. }else {
    51. //唤醒别人,等待自己,(用锁对象调用)
    52. this.notifyAll ();//先唤醒 后等待
    53. this.wait ();
    54. }
    55. } catch (Exception e) {
    56. e.printStackTrace ();
    57. }
    58. }
    59. //取包子
    60. //吃货1,吃货2
    61. public synchronized void get() {
    62. try {
    63. String name = Thread.currentThread ().getName ();
    64. if (list.size ()==1){
    65. //有包子,吃掉
    66. System.out.println (name+"吃了"+list.get ( 0 ));
    67. list.clear ();
    68. Thread.sleep ( 1000 );
    69. //唤醒别人,等待自己,(用锁对象调用)
    70. this.notifyAll ();//先唤醒 后等待
    71. this.wait ();
    72. }else {
    73. //没有包子
    74. //唤醒别人,等待自己,(用锁对象调用)
    75. this.notifyAll ();//先唤醒 后等待
    76. this.wait ();
    77. }
    78. } catch (Exception e) {
    79. e.printStackTrace ();
    80. }
    81. }
    82. }

    三、线程池

    1.什么是线程池

    2.线程池的工作原理

    控制线程的数量,也控制任务的数量,避免系统创建太多线程,耗费大量的系统资源。挺高系统性能。

    3.如何创建线程池

    1.方式1(ThreadPoolExecutor对象创建)

    1. import java.util.concurrent.*;
    2. public class ThreadPoolTest {
    3. public static void main(String[] args) {
    4. // public ThreadPoolExecutor(int corePoolSize,
    5. // int maximumPoolSize,
    6. // long keepAliveTime,
    7. // TimeUnit unit,
    8. // BlockingQueue workQueue) {
    9. // this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    10. // Executors.defaultThreadFactory(), defaultHandler);
    11. //1.通过ThreadPoolExecutor 创建一个线程池对象
    12. ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
    13. new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
    14. }
    15. }

    2.方式2(Executors创建)

    1. import java.util.concurrent.*;
    2. public class Test {
    3. public static void main(String[] args) throws Exception {
    4. // //1.通过ThreadPoolExecutor 创建一个线程池对象
    5. // ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
    6. // new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
    7. //1-2 通过Executors创建一个线程对象。
    8. ExecutorService pool = Executors.newFixedThreadPool ( 3 );//新建固定数量线程的线程池
    9. //计算密集型任务:核心线程数量=CPU核数+1;
    10. //IO密集型任务:核心线程数量=CPU核数*2;
    11. Executors.newSingleThreadExecutor ();//新建单个线程数量的线程池
    12. //2.使用线程处理Callable 任务类
    13. Future f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
    14. Future f2 = pool.submit ( new MyCallable ( 200 ) );
    15. Future f3 = pool.submit ( new MyCallable ( 300 ) );
    16. Future f4 = pool.submit ( new MyCallable ( 400 ) );
    17. System.out.println ( f1.get () );
    18. System.out.println ( f2.get () );
    19. System.out.println ( f3.get () );
    20. System.out.println ( f4.get () );
    21. }
    22. }

    3.注意事项

    4.线程池处理Runnable任务

    1.常用方法

    2.新任务的拒绝策略

    3.代码

    1. public class MyRunnable implements Runnable{
    2. @Override
    3. public void run() {
    4. //描述任务
    5. System.out.println (Thread.currentThread ().getName ()+"====>666");
    6. try {
    7. Thread.sleep ( Integer.MAX_VALUE );
    8. } catch (InterruptedException e) {
    9. e.printStackTrace ();
    10. }
    11. }
    12. }
    13. import java.util.concurrent.*;
    14. public class ThreadPoolTest {
    15. public static void main(String[] args) {
    16. // public ThreadPoolExecutor(int corePoolSize,
    17. // int maximumPoolSize,
    18. // long keepAliveTime,
    19. // TimeUnit unit,
    20. // BlockingQueue workQueue) {
    21. // this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    22. // Executors.defaultThreadFactory(), defaultHandler);
    23. //1.通过ThreadPoolExecutor 创建一个线程池对象
    24. ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
    25. new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
    26. Runnable target = new MyRunnable ();
    27. pool.execute ( target );//把任务交给线程池,线程池会自动创建一个新的线程,自动处理这个任务,自动执行。
    28. pool.execute ( target );
    29. pool.execute ( target );
    30. //临时线程的创建时机
    31. pool.execute ( target );//复用前面的核心线程执行任务
    32. pool.execute ( target );
    33. //开始进入任务队列
    34. pool.execute ( target );
    35. pool.execute ( target );
    36. pool.execute ( target );
    37. pool.execute ( target );
    38. //开始拒绝策略(任务队列占满)
    39. pool.execute ( target );//拒绝直接抛出错误
    40. // pool.shutdown ();//等线程池的任务全部执行完毕后,才会关闭线程池
    41. // pool.shutdownNow ();//立刻关闭线程池,不管任务是否执行完毕
    42. }
    43. }

    5.线程池处理Callable任务

    1.常用方法

     2.代码

    1. import java.util.concurrent.Callable;
    2. public class MyCallable implements Callable {
    3. private int n;
    4. public MyCallable() {
    5. }
    6. public MyCallable(int n) {
    7. this.n = n;
    8. }
    9. @Override
    10. public String call() throws Exception {
    11. int sum=0;
    12. for (int i = 0; i <=n; i++) {
    13. sum+=i;
    14. }
    15. return Thread.currentThread ().getName ()+"求出了1-"+n+"的和是:"+sum;
    16. }
    17. }
    18. import java.util.concurrent.*;
    19. public class Test {
    20. public static void main(String[] args) throws Exception {
    21. //1.通过ThreadPoolExecutor 创建一个线程池对象
    22. ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
    23. new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
    24. //2.使用线程处理Callable 任务类
    25. Future f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
    26. Future f2 = pool.submit ( new MyCallable ( 200 ) );
    27. Future f3 = pool.submit ( new MyCallable ( 300 ) );
    28. Future f4 = pool.submit ( new MyCallable ( 400 ) );
    29. System.out.println ( f1.get () );
    30. System.out.println ( f2.get () );
    31. System.out.println ( f3.get () );
    32. System.out.println ( f4.get () );
    33. }
    34. }

    四、并发、并行

    1.进程和线程的关系

    2.什么是并发?

    3.什么是并行?

    4.多线程是怎么执行的?

    五、线程的生命周期

           人的生命周期

    sleep(不会释放锁)   wait(会释放锁)

    六、悲观锁和乐观锁

    1.悲观锁:

    一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
    1. public class MyRunnable implements Runnable{
    2. private int count;
    3. @Override
    4. public void run() {
    5. for (int i = 0; i <100; i++) {
    6. //悲观锁
    7. synchronized (this) {
    8. System.out.println ("count=====>"+ (++count));
    9. }
    10. }
    11. }
    12. }
    13. public class Test1 {
    14. public static int Number;
    15. public static void main(String[] args) {
    16. //悲观锁,乐观锁的原理
    17. //悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
    18. //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
    19. //需求:1个变量,100个线程,每个线程对其加100次
    20. Runnable target =new MyRunnable ();
    21. for (int i = 1; i <=100; i++) {
    22. new Thread (target).start ();
    23. }
    24. }
    25. }

    2.乐观锁

    利用原子类实现

    乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
    1. public class MyRunnable2 implements Runnable{
    2. //乐观锁 CAS算法 比较(Compare)A(and)S(修改Set) 算法
    3. //整数修改的乐观锁:原子类实现的
    4. private AtomicInteger count=new AtomicInteger ();
    5. @Override
    6. public void run() {
    7. for (int i = 0; i <100; i++) {
    8. System.out.println ("count=====>"+ count.incrementAndGet ());//先加1然后再返回值
    9. }
    10. public class Test2 {
    11. public static void main(String[] args) {
    12. //悲观锁,乐观锁的原理
    13. //悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
    14. //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
    15. //需求:1个变量,100个线程,每个线程对其加100次
    16. Runnable target =new MyRunnable2 ();
    17. for (int i = 1; i <=100; i++) {
    18. new Thread (target).start ();
    19. }
    20. }
    21. }

  • 相关阅读:
    为啥50岁以后,病就增多了?中老年人想要少生病,该做些什么?
    人类或小鼠染色体长度文件获取 hg19 mm10.chrom.sizes
    【Redis】基本操作 - 安装与配置
    【学习】一致性哈希与哈希环
    《Python》典型数据结构
    无密码解锁iPhone
    【深度学习】- NLP系列文章之 1.文本表示以及mlp来处理分类问题
    KNN分类算法
    应用案例 | 基于三维机器视觉的机器人麻袋拆垛应用解决方案
    Linux中安装mysql8
  • 原文地址:https://blog.csdn.net/m0_64703222/article/details/133124301