600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Java线程 Java多线程详细介绍

Java线程 Java多线程详细介绍

时间:2020-10-26 17:58:04

相关推荐

Java线程 Java多线程详细介绍

目录

一、进程和线程的区别

1.1 进程

1.2 线程

二、并发和并行

2.1 并行

2.2 并发

2.3 监控线程的执行情况

三、创建方式

3.1 继承Thread类

思考:为什么不直接通过对象调用start()方法?

3.2 实现Runnable接口

3.3 使用Callable创建线程

3.4 Callable 和 Runnable的区别

继承Thread和实现Runnable接口两种方法的区别

3.5使用线程池例如Executor框架

3.5.1 概念

3.5.2 好处

3.5.3 线程池执行基本原理

3.5.4 线程池创建方式

3.5.5 线程池的分类

3.5.6 线程池的核心参数

四、常用方法

五、多线程实现方案

六、线程终止

七、常用方法

八、线程的生命周期

8.1 线程的状态

8.2 wait()、notify()、notifyAll()类比

九、 线程安全

9.1 安全问题原因​编辑

9.2 synchronized

9.3 Lock

9.4 线程锁

9.5 并发工具类 ConcurrentHashMap HashMap HashTable

9.6 线程死锁

9.7 释放锁

十、思考题

1.volatile关键字

2.count++ 是不是原子操作

3.怎么保证原子操作

4.原子类原理--CAS原理

5.ConcurrentHashMap HashMap 和HashTbale的区别

一、进程和线程的区别

1.1 进程

操作系统中正在运行的软件,比如我们使用的QQ,就启动了一个进程,操作系统就为该进程分配了新的内存空间,进程具有独立性,动态性和并发性

独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位动态性:进程的实质是程序的一次执行过程,进程是动态产生的,动态是消亡的并发性:任何进程都可以同其它进程一起并发执行

1.2 线程

线程是由进程创建的,是进程的一个实体,一个进程可以拥有多个线程。是进程中的单个顺序控制流,是一条执行路径。就是应用程序中做的事情。比如:360软件中的杀毒,扫描木马,清理垃圾

单线程

同一个时刻只允许执行一个线程

多线程

同一个时刻,可以执行多个线程

线程

用户线程

也称为工作线程,当线程的任务执行完或以通知的方式结束

守护线程

一般是为工作线程服务的,当所有的用户线程结束后,守护线程自动结束,常见的守护线程有 垃圾回收机制

应用:如果我们希望当main线程结束之后,子线程自动结束,只需要将子线程设置为守护线程即可。设置方法 实例.setDaemon(),位置在实例.start()之前

注意:一个进程至少包括一个线程

二、并发和并行

2.1 并行

在同一时刻,有多个指令在多个CPU上同时执行

造成一种 貌似同时 的错觉,简单的说,单核cpu实现的多任务就是并行

2.2 并发

在同一时刻,有多个指令在多个CPU上交替执行

多核cpu可以执行并行

Notes:并发、并行可以同时发生

2.3 监控线程的执行情况

在终端输入Jconsole可以监控线程的执行情况

三、创建方式

3.1 继承Thread类

在继承Thread类后,该类就可以当做线程使用。往往要重写run()(Thread实现了Runnable接口),在此方法内写入自己的业务逻辑,运行需要创建该类的实例,通过实例调用start方法。

思考:为什么不直接通过对象调用start()方法?

当我们运行一个程序时相当于启动了一个线程,当进程启动了以后,在进程开了一个主线程,当main线程执行 实例.start()主线程不会阻塞,会继续执行,如果直接去调用run(),此时的run()就是一个普通的方法,会发生阻塞,没有真正的开启一个线程,只有将run()执行完之后才会去执行其他的语句。真正实现多线程效果的是native 方法 start0()而不是run()。注start0()是由JVM调用,底层是c/c++来实现的(主线程执行完任务就会结束的,但是有子线程在运行时,进程也不会结束的,当所有的线程结束了,进程就结束了)

3.2 实现Runnable接口

Notes :java是单继承的,在某些情况下一个类可能已经继承了某个父类,这是再用继承Thread类方法来创建线程显然是已经不可能的了。因此Java设计者们提供了另一个方法创建线程,就是通过实现Runnable接口来实现方法

运行需要新建一个线程,将实现Runnable接口的方法的类的实例放入线程的有参构造的方法,这里是使用设计模式的代理模式 。

3.3 使用Callable创建线程

和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

3.4 Callable 和 Runnable的区别

call()方法可以有返回值 call()方法可以声明抛出异常callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,try catch,但是runnable接口实现类中run方法的异常必须在内部处理,不能抛出

callable和runnable都可以应用于executors。而thread类只支持runnable,

继承Thread和实现Runnable接口两种方法的区别

从java的设计来看,通过继承Thread或者实现Runnale接口来创建线程本质上没有区 别,Thread本质上也是实现了Runnable接口。实现Runnbale接口的方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制

3.5使用线程池例如Executor框架

3.5.1 概念

盛放线程的容器,盛放线程的池子

3.5.2 好处

1. 减少频繁创建及销毁线程的操作,节省资源

2. 线程池会将闲置的线程处理任务,达到线程复用

3.5.3 线程池执行基本原理

1. 创建一个池子,此时没有线程 pool.getPoolSize()

2. 当第一次提交任务给线程池,线程池就会创建线程执行任务。当任务执行完毕。线程对象归还给池子线程并不会消亡

3.当再次提交任务,此时线程池就会将闲置的线程去处置任务,如果没有闲置的线程,才会创建新的线程

3.5.4 线程池创建方式

创建线程池

ExecutorService pool = Executors.newCachedThreadPool();

线程池最多创建int最大值个线程

ExecutorService pool = Executors.newFixedThreadPool(3);

创建线程池对象,规定最大数

创建一个线程池,根据需要创建新的线程,但在可用时将重用先前构建的线程

方式一

1. 创建一个任务对象

MyTarget myTarget = new MyTarget();

2. 提交任务给线程池

pool.submit(myTarget);

过去的方式 Thread t1 = new Thread(myTarget);

缺点:自己创建线程,执行完任务,消亡浪费资源,系统资源开销大

3. 如果不需要线程池,将其关闭

pool.shutdown();

方式二

匿名内部类

3.5.5 线程池的分类

3.5.6 线程池的核心参数

1. int corePoolSize

线程池核心线程大小,如果你的任务数量小于核心线程数,那么开启的线程数量还是三个

2.int maximumPoolSize

线程池最大线程数,基于KeepaliveTime会销毁

3.KeepaliveTime

空闲线程存活时间

4.TimeUnit 时间单位

时间单位,为 keepAliveTime 指定时间单位

5.workQueue 阻塞队列,用于保存任务的阻塞队列

1). ArrayBlockQueue 基于数组的有界队列,FIFO,防止资源耗尽问题

2). LinkedBlockQueue基于链表的无界阻塞队列,界值为Integer.MAX FIFO

Tips:maximumPoolSize参数是没有用的

3). SynchronousBlockQueue(不缓存任务队列)

Tips:maximumPoolSize参数是有用的,如果超过了会执行拒绝策略

4). ProprityBlockQueue() 几区Comparator实现,比较 无界阻塞队列

6.ThreadFactory

创建线程的工程类,例如守护线程daemon

7.RejectedExecutionHandler handler 拒绝策略

1). callerrunsPolicy(除非线程池shutdown,抛弃任务)

2). AbortPolicy(直接抛出异常)

3). DiscardPolicy(什么都不做,直接丢弃任务) //不建议用

4). DiscardOldestPolicy(什么都不做,直接丢弃队列里面最早的任务)

四、常用方法

用户线程:也叫工作线程,当线程的任务执行完或以通知方式结束

守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

常见的守护线程:垃圾回收机制

五、多线程实现方案

六、线程终止

七、常用方法

八、线程的生命周期

8.1 线程的状态

8.2 wait()、notify()、notifyAll()类比

九、 线程安全

9.1 安全问题原因

解决方式

1.同步代码块

2.同步方法

3.Lock锁

9.2 synchronized

1、 Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。

2、每个对象都对应一个可称为互斥锁的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象。

3. 关键字sychronized来与对象的互斥锁联系,当某个对象用sychronized修饰时,表名该对象在任一时刻只能由一个线程访问。

4.同步的局限性:导致程序的执行效率要降低

5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)

6. 同步方法(静态的)的锁为当前类本身

Java中static作用及用法详解_CrazyCodeBoy的博客-CSDN博客_java static

被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。

被static关键字修饰的属性或方法不属于实例化的对象,而是属于类,而this属于当前操作的实例对象,所以同步static锁在了类上

public class demoSell{//静态方法public synchronized static void method1(){}//静态方法中的同步代码块public static void mehtod2(){synchronized(demoSell.class){}}}

注意:

1.同步方法如果没有使用static修饰:默认所对象为this

2.如果方法使用static修饰,默认锁对象:当前类.class

3.实现的落地步骤

·需要先分析上锁的代码

·选择同步代码块或同步方法

·要求多个线程锁对象为同一个即可

1.同步代码块

synchronized(对象){//得到对象的锁,才能操作同步代码块//需要被同步代码}

2. sychronized还可以放在方法声明中,表示整个方法为同步方法

public synchronized void method(String name){//需要被同步的代码}

9.3 Lock

//创建可重复锁Lock lock = new ReentrantLock();//上锁lock.lock();//代码块.............//释放锁lock.unlock();

9.4 线程锁

9.5 并发工具类 ConcurrentHashMap HashMap HashTable

9.6 线程死锁

概念:

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程一定要避免死锁的发生

9.7 释放锁

1. 当前线程的同步方法,同步代码块执行结束后会自动释放锁

2. 当前线程在同步代码块,同步方法中遇到break、return

3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束

4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停并且释放锁

注意:下面的情况不会释放锁

1.线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。

2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁,应该避免使用suspend()和resume()来控制线程,方法不再推荐使用。

十、思考

1.volatile关键字

在多线程的情况下保证了数据的可见性,加了volatile的属性,一个线程修改后,另一个线程也能看到

原理:一个线程修改了数据,就会把数据刷新到主存中,其他线程读取到的副本就会失效,重新读取主存中的新数据

2.count++ 是不是原子操作

不是,三步操作,:

1.从主存中count的值存到A线成独有的空间

2.在线程独有工作空间中执行+1操作

3.将101重新刷新relaod到主存中

随时可能丢失CPU的执行权,比如在执行+1操作还未刷新到主存,就被其他线程B抢占

3.怎么保证原子操作

1.同步代码块

2.原子类AtomicInteger

4.原子类原理--CAS原理

在修改主存中的值,先拿旧值和主存中的值判断

如果相等,则直接修改

如果不等,说明已经有其他线程修改过了,需要重新获取主存中的新值

这里涉及到三个变量

1. 旧值: 第一次获取主存中值时,作为旧值存放在该变量中, 比如100

2. 新值 该线程操作后的值,比如执行++操作后的101

3.要修改的值 主存里面的值

如果旧值和要修改的值相等,说明没有被修改过,可以直接赋值

5.ConcurrentHashMap HashMap 和HashTbale的区别

HashMap效率最高 但是不安全

HashTable 效率最低 安全 每个方法都加锁

ConcurrenetHashp 效率第二高 安全

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。