Java基础知识(12)

发布于:2024-05-10 ⋅ 阅读:(29) ⋅ 点赞:(0)

Java基础知识(12)

(包括:多线程)

目录

Java基础知识(12)

一.多线程

1. 多线程基础

2. 多线程的实现方式

【1】继承Thread类的方式进行实现

【2】实现Runnable接口的方式进行实现

【3】利用Callable接口和Future接口方式实现

3.多线程三种实现方式对比

4. 常见的成员方法

5.线程的生命周期

6. 同步代码块

7. 同步方法

8. Lock锁

9.死锁(这是一种错误)

10.生产者和消费者(等待唤醒机制)

11. 线程的状态

12. 线程池

[1]主要核心原理

[2] 线程池代码实现

【3】什么是最大并行数?

【4】线程池多大合适呢(了解)

13.多线程额外拓展(实际开发用的少,但面试的时候喜欢问)


一.多线程

1. 多线程基础

(1)什么是多线程

有了多线程,我们就可以让程序同时做多件事情

(2)多线程的作用:提高效率

(3)多线程的应用场景

只要你想让多个事情同时运行就需要用到多线程

比如:软件中的耗时操作、所有的聊天软件、所有的服务器

(2)并发和并行

1.并发:在同一时刻,有多个指令在单个CPU上交替执行

2.并行:在同一时刻,有多个指令在多个CPU上同时执行

2. 多线程的实现方式

【1】继承Thread类的方式进行实现

(1)自己定义一个类继承Thread

(2)重写run方法

(3)创建子类的对象,并启动线程

(4)案例

public class MyThread extends Thread{

@override

public void run(){

//书写线程要执行代码

for(inti=0;i<100;i++){

System.out.println("Helloworld");

}}}

public static void main(string[]args){

MyThread t1 = new MyThread();

//开启线程I

t1.start();

}

【2】实现Runnable接口的方式进行实现

(1)自己定义一个类实现Runnable接口

(2)重写里面的run方法

(3)创建自己的类的对象

(4)创建一个Thread类的对象,并开启线程

(5)案例

public class MyRun implements Runnable{

@0verride

public void run(){

//书写线程要执行的代码

for(inti=0;i<100;i++){

System.out.printin("HelloWorld!");

}}}

public static void main(string!args){

//创建MyRun的对象

//表示多线程要执行的任务

MyRun mr= new MyRun();

//创建线程对象

Thread t1 = new Thread(mr);

//开启线程

t1.start();

}

【3】利用Callable接口和Future接口方式实现

特点:可以获取到多线程运行的结果(重要)

(1)创建一个类MyCa11able实现Ca1lable接口

(2)重写ca11 (是有返回值的,表示多线程运行的结果)

(3)创建MyCallable的对象(表示多线程要执行的任务)

(4)创建FutureTask的对象(作用管理多线程运行的结果)

(5)创建Thread类的对象,并启动(表示线程)

(6)案例

public class Mycallable implements Callable<Integer>(

@0verride

public Integer call()throws Exception{

//求1~188之间的和

int sum =0;

for(inti=1;i<=100;i++){

sum = sum +i;

}

return sum;

}}

public static void main(string[]args){

//创建MyCallable的对象(表示多线程要执行的任务)

MyCallable mc = new MyCallable();

//创建FutureTask的对象(作用管理多线程运行的结果)

FutureTask<Integer>ft=new FutureTask<>(mc);

//创建线程的对象

Thread t1 = new Thread(ft);

//启动线程

t1.start();

//获取多线程运行的结果

Integer result=ft.get();

System.out.println(result);

}}

3.多线程三种实现方式对比

优点

缺点

继承Thread类

编程比较简单,可以直接使用Thread类中的方法

可以扩展性较差,不能再继承其他的类

实现Runnable接口

扩展性强,实现该接口的同时还可以继承其他的类

编程相对复杂,不能直接使用Thread类中的方法

实现Ca1lable接口

只有实现Ca1lable接口这种方法可以获取多线程的结果

4. 常见的成员方法

(1)String getName() 返回此线程的名称

(2)void setName(String name) 设置线程的名字(构造方法也可以设置名字)

(3)static Thread currentThread() 获取当前线程的对象

(4)static void sleep(long time) 让线程休眠指定的时间,单位为毫秒

(5)setPriority(int newPriority) 设置线程的优先级

(最小1,最大10,默认5)

(优先级越高只是说明抢到线程的概率高,但并不绝对)

(6)final int getPriority() 获取线程的优先级

(7)final void setDaemon(boolean on) 设置为守护线程(true为设置)

(细节:当其他的非守护线程执行完毕之后,守护线程会陆续结束)

(8)public static void yield() 出让线程/礼让线程 

(当该线程执行后会出让CPU执行权,然后所有线程重新抢夺执行器,会让线程执行的更均匀一点。)

(9)public static void join() 插入线程/插队线程

5.线程的生命周期

sleep方法会让线程睡眠,睡眠时间到了之后,立马就会执行下面的代码吗?

不会,会进入就绪态等待获得执行权。

6. 同步代码块

(1)把操作共享数据的代码锁起来

(2)格式:

synchronized(锁){

操作共享数据的代码

}

//锁对象,一定要是唯一的:static object obj= new object();

特点1:锁默认打开,有一个线程进去了,锁自动关闭

特点2:里面的代码全部执行完毕,线程出来,锁自动打开

7. 同步方法

(1)就是把synchronized关键字加到方法上

(2)格式:

修饰符 synchronized 返回值类型 方法名(方法参数) {..}

特点1:同步方法是锁住方法里面所有的代码

特点2:锁对象不能自己指定,由Java指定

非静态方法: this(锁对象)

静态方法:当前类的字节码文件对象(锁对象)(一般用这个!!!)

(3)StringBuilder和StringBuffer

两者的方法基本相同,但StringBuilder在多线程时不安全,此时应用StringBuffer

8. Lock锁

(1)产生原因

虽然我们可以理解同步代码块和同步方法的锁对象问题

但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

(2)方法

Lock中提供了获得锁和释放锁的方法

void lock():获得锁 手动上锁

void unlock():释放锁 手动释放锁

(3)创建

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

ReentrantLock的构造方法

ReentrantLock():创建一个ReentrantLock的实例

(4)案例

public void run(){

while(true){

//同步代码块

Lock.lock();//2 //3

try {

if(ticket == 100){

break;

}else{

Thread.sleep(10);

ticket++;

System.out.println(getName())

}

}catch(InterruptedException e){

e.printstackTrace();

} finally{

Lock.unlock();

}

Try-catch-finally可以确保一定会释放锁。

9.死锁(这是一种错误)

以后注意千万不要让两个锁嵌套起来

10.生产者和消费者(等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式

[1]消费者执行过程

(1)判断東子上是否有食物

(2)如果没有就等待

(3)如果有就开吃

(4)吃完之后,唤醒厨师继续做

[2] 生产者执行过程

(1)判断桌子上是否有食物

(2)有:等待

(3)没有:制作食物

(4)把食物放在桌子上

(5)叫醒等待的消费者开吃

[3] 生产者和消费者(常见方法)

void wait() 当前线程等待,直到被其他线程唤醒

void notify() 随机唤醒单个线程

void notifyA11() 唤醒所有线程

synchronized(Desk.lock){

Desk.lock.wait();//让当前线程跟锁进行绑定,不然一会唤醒总不能把电脑所以进程都唤醒吧

Desk.lock.notifyA11();

}

[3] 等待唤醒机制(阻塞队列方式实现)

(1)put数据时:放不进去,会等着,也叫做阻塞。

(2)take数据时:取出第一个数据,取不到会等着,也叫做阻塞。

(3)阻塞队列内部有锁,再把它写在里面就会形成死锁

(4)ArrayBlockingQueue:底层是数组,有界

(5)LinkedBlockingQueue:底层是链表,无界,但不是真正的无界,最大为int的最大值。

(6)细节:生产者和消费者必须使用同一个阻塞队列(在main中创建)

11. 线程的状态

(1)新建状态(NEW)    --> 创建线程对象

(2)就绪状态(RUNNABLE)      à start方法

(3)阻塞状态(BLOCKED)       à无法获得锁对象

(4)等待状态(WAITING)        àwait方法

(5)计时等待(TIMED WAITING)       àsleep方法

(6)结束状态(TERMINATED)       à全部代码运行完毕

12. 线程池

[1]主要核心原理

① 创建一个池子,池子中是空的

② 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子

下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可

③ 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

[2] 线程池代码实现

(1)步骤

①创建线程池

②提交任务

③所有的任务全部执行完毕,关闭线程池(一般不会关闭)

(2)方法

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池(最大为int的最大值。)

public static ExecutorService newFixedThreadPool(int nThreads) 创建有上限的线程池

(3)案例

//1.获取线程池对象

ExecutorService pool1 = Executors.newCachedThreadPool()

//2.提交任务

poo11.submit(new MyRunnable());

//3.销毁线程池

poo11.shutdown();

【3】什么是最大并行数?

如:电脑为4核8线程,则最大并行数为8

//向Java点拟机返回可用处理器的数目

int count =Runtime.getRuntime().availableProcessors();

System.out.println(count);

即可获取本电脑的最大并行数

【4】线程池多大合适呢(了解)

CPU 密集型运算 最大并行数 +1(计算多)

I/0 密集型运算 最大并行数*期望CPU 利用率*总时间(CPU计算时间+等待时间)/ CPU计算时间(读取数据多)

13.多线程额外拓展(实际开发用的少,但面试的时候喜欢问)

在仅我可见中


网站公告

今日签到

点亮在社区的每一天
去签到