多线程--MultiThread
Posted 耍流氓的兔兔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程--MultiThread相关的知识,希望对你有一定的参考价值。
线程间的任务不同,但是线程操作的数据相同
有共享数据
操作共享数据的代码超过一句
多线程:
多线程的程序可以包括多个顺序执行流,多个顺序流之间互不干扰
实现同时执行的效果,实际上是多个线程在争抢CPU
可以实现不同功能的同时执行
多线程不一定能够提高效率,但是可以合理的使用PCU资源
多线程的运行结果是不同的,因为线程在争抢CPU,这就是多线程的随机性
进程(Process):
每个运行中的程序就是一个进程
正在运行的程序,也就是在内存中开辟的内存空间
线程(Thread):
负责程序执行的一条执行路径,也称为一个执行单元
进程的执行实际上是线程在执行
一个进程至少会有一个线程(Main Thread),主线程执行main函数代码
当一个进程中有多个线程时,就是多线程程序
线程是随着任务的存在而存在,随着任务的结束而消失
任务(Task):
每个线程需要执行的代码
任务代码都有其存储位置
主线程的任务代码在main函数,垃圾回收线程的任务代码在finalize函数中
JVM(Java 虚拟机)是不是多线程? 至少有一个负责正常执行的线程,也就是执行main函数中的代码----主线程 还有一个负责垃圾回收的线程,也就是执行finalize函数中的代码----垃圾回收线程 JVM在后台提供了一个超级线程来执行垃圾回收 每个对象都可以被回收,回收的功能定义在Object的finalize()方法中 运行垃圾回收器,调用System的gc()方法
创建线程:
创建线程的第一种方式:
1 创建一个类继承Thread 2 重写Thread类中的run方法 创建线程是为了执行任务 任务代码必须有存储位置,run方法就是任务代码的存储位置 3 创建子类对象,实际在创建线程 4 启动线程 主线程的任务代码在main函数中 子线程的任务代码在run函数中
run()方法只是一个普通的方法调用,不具备启动线程的功能
start()方法会启动线程并执行run()中的代码
创建线程的第二种方式:
为了解决临界资源的问题,需要使用创建线程的第二种方式
1 创建实现了Runnable接口的子类
2 重写Runnable接口的run方法
3 创建实现了Runnable接口的子类的对象
4 创建Thread类的对象,也就是创建线程
5 把实现了Runnable接口的子类对象作为参数传递给Thread的构造方法
优点:
把线程任务进行了描述,也就是面向对象
实现了线程任务和线程对象的分离,线程执行什么任务不再重要,只要实现了Runnable接口的子类对象都可以作为参数传递给Thread的构造方法
实现了接口的同时还可以继承父类
创建线程的第三种方式:
1 创建Callable接口的实现类
2 重写Callable接口的call方法
3 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该call方法的返回值
4 将FutureTask对象作为参数传递给Thread的构造方法
5 使用FutureTask对象的get方法获得子线程结束后的返回值
总结:
通过继承Thread类和或者实现Runnable接口,Callable接口都可以实现多线程
通过接口实现的两种方式相似,只是Callable接口定义的方法有返回值
使用继承类和接口创建多线程的方式对比:
Runnable和Callable线程类只是实现了Runnable和Callable接口,还可以继承其他类
多个线程可以共享同一个对象,可以处理同一份资源
访问当前线程必须使用Thread.currentThread方法
Thread线程类已经继承了Thread,不能再次继承其他类
访问当前线程只需this
为什么不能直接创建Thread类,然后调用start方法? 任务代码必须写在run方法中,而Thread类中的run方法没有实现任何功能,代码执行没有结果 所以只有继承Thread类,重写run方法
多线程内存:
在栈中为主线程开辟内存
多线程不遵循先进后出,每个线程在栈中都有内存,谁先获得CPU,就先执行
当线程执行完自己的任务代码,线程就从栈中消失
只有所有线程都结束,整个进程才结束
synchronized:
synchronized代码块的锁:obj synchronized同步函数的锁:this 静态同步函数: 静态函数进内存的时候不存在对象,但是存在其所属类的字节码文件,属于Class类型的对象,所以静态同步函数的锁是其所属类的字节码文件对象 避免死锁: 容易发生在锁的嵌套
线程间的通信:
线程间的任务不同,但是线程操作的数据相同
有共享数据
操作共享数据的代码超过一句
等待唤醒机制:
必须用在同步中,因为同步中才有锁
指明让持有锁的线程去等待或被唤醒
等待的线程会放弃锁
wait(),notify(),notifyAall()为什么定义在Object中? wait(),notify(),notifyAall()必须用在同步中,因为同步中才有锁 锁是任意对象,任意对象都可以调用的方法需要定义在Obbject中
jdk1.5 多线程的实现方式:
jdk1.5之前: 对锁的操作是隐式的 synchronized(obj)//获取锁 { }//释放锁 jdk1.5之后: import.util.concurrent.Locks; 有一个描述锁的Lock接口,锁被面向对象 使用Lock的子类ReentrantLock创建一把锁
使用Lock替代同步代码块的方式:
使用Lock替代同步代码块的方式: 1 创建一把锁 2 把之前写在同步代码块中的代码写在lock()和unlock()之间 lock()获取锁 unlock()释放锁 await()等待 单独的定义在Condition接口中 也被面向对象 signal()唤醒一个等待线程 signalAll()唤醒所有等待线程 3 得到一个和锁绑定的Condition对象 jdk1.5对唤醒等待方法也进行了单独的描述,描述的接口是Condition 唤醒等待方法必须结合着锁来使用,所以使用Lock的newCondition()方法来获得和锁绑定的对象
用Lock替代同步代码块之后出现了IllegaMonitorStateException(无效的监视器异常)?
因为wait(),notify(),notifyAll()必须用在同步代码块中,而同步代码块被Lock替代掉了
守护线程:
垃圾回收线程就是一个守护线程 可以看成后台线程,依赖于前台线程 当前台线程全部结束时,即使守护线程的任务代码没有执行完也会立刻结束 在线程启动前设置启动线程 setDaemon()设置为守护线程 join()加入运行,主线程会让着此线程,等到此线程运行完主线程才会继续执行
线程池:
java.util.concurrent 可以创建线程的对象 接口:ExeutorService 创建线程池: execute()开始执行任务 创建只有一个线程的线程池 任务只能一个一个执行,按照任务提交的先后顺序执行 newSingleThreadExecutor() 创建固定个数的线程池 任务数量和线程数量相同时,任务会同时执行 任务数量大于线程数量时,多出来的任务会等待线程池中已有的线程空闲下来去执行 newFixedThreadPool() 创建缓冲的线程池 自身会创建一定数量的线程 线程数量大于任务数量,会自动销毁空闲的线程 当任务增加时又会自动增加线程 newCachedThreadPool() 创建数量无限的线程池 空闲的线程不会被销毁 newScheduledThreadPool()
使用匿名内部类创建线程:
new Thread() { public void run() { } }.start(); new Thread(new Runnable() { public void run() { } }).start(); Runnable r = new Runnable() { public void run() { } }; new Thread(r).start();
以上是关于多线程--MultiThread的主要内容,如果未能解决你的问题,请参考以下文章
MultiThread(VS2013 MFC多线程-含源码-含个人逐步实现文档)