java小白从入门到精通(基础十七)

1. 线程池

如果我们用前面学习的知识,要创建100个线程那么就得new100次

线程的创建是非常耗费资源和耗时的

1.1 线程池有什么用?

  • 解决了系统频繁的创建线程的资源消耗

  • 达到了线程对象的重用

  • 线程池可以保障系统的安全

1.2 使用线程池创建线程

//创建单线程的线程池(借助jdk中给我们提供的工具类Executors)
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
//创建固定大小的线程池
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
//创建弹性伸缩的线程池
ExecutorService executorService3 = Executors.newCachedThreadPool();

1.3 使用线程池对象创建和执行线程

package com.uplooking.demo02;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
     

    public static void main(String[] args) {
     
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(8);
        //使用线程池对象执行线程
        executorService.execute(new Runnable() {
     
            @Override
            public void run() {
     
                while (true) {
     
                    System.out.println("线程1");
                }
            }
        });
        //执行任务(线程池会帮我们创建线程)
        executorService.execute(new Runnable() {
     
            @Override
            public void run() {
     
                while (true) {
     
                    System.out.println("线程2");
                }
            }
        });


        //关闭线程池(等待线程池中的线程执行完毕之后才会关闭线程池)
        //executorService.shutdown();

        //立刻关闭线程池(关闭的是等待状态线程池中的线程)
        //executorService.shutdownNow();
    }
}

1.4 创建延时任务的线程池

package com.uplooking.demo03;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Main {
     
    public static void main(String[] args) {
     
        //创建延时任务的线程池对象
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(8);
        scheduledExecutorService.schedule(new Runnable() {
     
            @Override
            public void run() {
     
                System.out.println("hello up");
            }
        }, 3000, TimeUnit.MILLISECONDS);

        //关闭线程池
        scheduledExecutorService.shutdown();
    }
}

2. 常用的线程池是哪个?

FixedThreadPool(n)

n到底为多少性能才能发挥到最好?

n为cpu的核数性能最优

3. 线程是不是创建的越多越好?

不是

对单核的cpu而言,只能并行的执行一个线程,

对于8核的cpu而言,只能最多并行执行8个线程

最多为cpu的核数就可以了

4. 使用多线程的好处与弊端

好处:

  • 提升程序的执行效率

弊端:

  • 资源有额外的占用
  • 线程会造成数据安全性问题(线程安全性问题)

5. 线程安全性问题

java小白从入门到精通(基础十七)_第1张图片

线程安全问题: 多个线程同时(并行)操作同一个数据的时候出现的数据的安全性问题

5. 售票代码实现

5.1 线程安全性问题的讲解

java小白从入门到精通(基础十七)_第2张图片

5.2 解决线程的安全性问题

核心思想: 对多个线程操作(全局)共享变量的代码,只能同时让一个线程访问

给操作全局变量的代码加锁

5.2.1 加锁一定好吗?

凡是加锁,效率必然会受影响,我们问题是一定要解决的,我们能做的就是尽量提高效率;

6. java中的线程锁的分类

  • 悲观锁
  • 乐观锁
  • 可重入锁
  • 可选锁
  • aqs锁

7. java中的加锁的方式(面试几率极高)

  • lock锁(手动挡): 锁的获取和释放都需要手动的进行
  • **synchronized锁(自动挡)*锁的获取和释放都是(api)自动进行

8. Synchronized锁

8.1 同步(Synchronized)方法

java小白从入门到精通(基础十七)_第3张图片

锁对象: 调用当前(run)方法的对象

上面我们加的锁是属于: 悲观锁,而我们在实际的生产环境中,绝对要避免悲观锁的存在

同步方法的锁对象不能改变,只能是调用当前方法的对象

8.2 同步(Synchronized)代码块

java小白从入门到精通(基础十七)_第4张图片

同步代码快的锁对象可以自己任意定义,但是要保证有意义;

9. 可重入锁(面试高频问题)

java小白从入门到精通(基础十七)_第5张图片

上面这个代码是可以正常执行的,;那就说明同一个锁对象可能被同一个线程对象获取了两次;

同一个锁对象被获取两次,那么加锁毫无意义了;

上面的描述经过我刚才的分析发现是错误的;

9.1 锁的可重入性

锁是可以被传递的

​ 在一个锁中调用另外一个锁的代码**,这两个锁是同一个锁**,那么锁是可以被传递的;

一般我们设计的优秀的锁,都要具有传递性

一般我们设计的优秀的锁,都要是可重入锁

10. Lock锁

相当于手动挡的车:锁的创建,获取和解锁都要自己来干;

好处: 拥有极高的自定义性

弊端: 对编程能力要求高

10.1 Lock锁的分类

  • 读锁
  • 写锁
  • 可重入锁
    java小白从入门到精通(基础十七)_第6张图片

11. 线程死锁

你中有我,我中有你

锁A中调用锁B,

同时

锁B调用锁A

互相处于永久的等待释放状态

12. 线程死锁代码实现

package com.uplooking.demo07;

public class Main {
     

    public static Object lockObjA = new Object();
    public static Object lockObjB = new Object();

    public static void main(String[] args) {
     
        RunnableA runnableA = new RunnableA();
        RunnableB runnableB = new RunnableB();

        new Thread(runnableA, "窗口A").start();
        new Thread(runnableB, "窗口B").start();

    }
}

class RunnableA implements Runnable {
     
    @Override
    public void run() {
     
        synchronized (Main.lockObjA) {
     
            System.out.println("lockObjA " + Thread.currentThread().getName());
            try {
     
                //避免直接获取到lockB这个锁
                Thread.sleep(2000);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            synchronized (Main.lockObjB) {
     
                System.out.println("lockObjB ");
            }
        }

    }
}


class RunnableB implements Runnable {
     
    @Override
    public void run() {
     
        synchronized (Main.lockObjB) {
     
            System.out.println("lockObjB " + Thread.currentThread().getName());
            try {
     
                Thread.sleep(2000);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            synchronized (Main.lockObjA) {
     
                System.out.println("lockObjA ");
            }
        }
    }
}

13. 死锁问题如何解决

java小白从入门到精通(基础十七)_第7张图片

通过sun公司提供的性能分析工具 jconsole来监测死锁,监测出来之后就可以修改死锁代码;

14. jvm内存中的栈

栈: 线程栈
java小白从入门到精通(基础十七)_第8张图片

  • 每个线程都拥有自己的内存空间和资源,互不影响
  • 栈中的数据是线程独占的,堆中的数据是线程共享的;
  • 所以栈中定义的局部变量不存在线程安全性问题,因为是线程独占的,而堆中的数据存在线程安全性问题,因为堆的数据是线程共享的;

15. 多线程开发的注意问题

  • 尽量使用线程池技术创建和管理线程
  • 能不使用全局共享变量尽量不要使用
  • 要是使用全局共享变量,并且多个线程同时写操作,解决线程安全问题,尽量不要使用悲观锁;
  • 能使用synchronized尽量不要使用lock锁;

你可能感兴趣的:(javase笔记,代码,java)