概述
线程简介
- 进程
- 正在运行的程序。
- 进程是系统进行资源分配和调用的独立单位。每一个进程都有他自己的内存空间和系统资源。
- 线程
- 在同一个进程内又可以执行多个任务。而这每一个任务就可以看成是一个线程。
- 线程是运行在进程中的一个独立实体,是CPU调度和分派的基本单位。
- 单线程:程序只有一条执行路劲。
- 多线程:程序有多条执行路劲。
- 多个线程会共享进程所拥有的全部资源。
- 多线程:为了提高应用程序的使用率。(程序的执行其实都是在抢CPU资源,CPU的执行权。多个进程在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。)通过多个线程并发执行,从而提高任务处理的速度。
- 线程的开销
- 关于时间:创建线程使用是直接向系统申请资源的,对操作系统来说,创建一个线程的代价是十分昂贵的, 需要给它分配内存、列入调度,同时在线程切换的时候还要执行内存换页,CPU 的缓存被 清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。
- 关于资源:Java线程的线程栈所占用的内存是在Java堆外的,所以是不受java程序控制的,只受系统资源限制,默认一个线程的线程栈大小是1M(当让这个可以通过设置-Xss属性设置,但是要注意栈溢出问题),但是,如果每个用户请求都新建线程的话,1024个用户光线程就占用了1个G的内存,如果系统比较大的话,一下子系统资源就不够用了,最后程序就崩溃了。
- 并行和并发
- 并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。
- 并发:物理上同时发生,指在某一个时间点同时运行多个程序。
- Q:JVM虚拟机的启动是单线程还是多线程?
- 多线程。
- 原因是垃圾回收线程也要先启动,否则很容易出现内存溢出。
- 垃圾回收线程+主线程,最低启动了两个线程,所以jvm的启动其实是多线程的。
多线程实现方式
- 实现原理
- 由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。 而进程是由系统创建的,所以我们应该去调用系统功能创建一个线程。
- java是不能直接调用系统功能的,所以,我们没办法直接实现多线程程序。但是,Java可以调用C/C++写好的程序来实现多线程程序。由C/C++去调用系统功能创建进程,然后由java去调用即可实现多线程程序。
- 实现方式
- 方式一:继承Thread类
- 方式二:实现Runnable接口
- 方式三:线程池Executors类
- 创建固定数目线程的线程池
- 创建一个可缓存的线程池(可大可小,随着任务量)
- 定时及周期性的执行任务的线程池
- scheduleAtFixedRate 这个方法是不管你有没有执行完,反正我每隔几秒来执行一次,以相同的频率执行
- scheduleWithFixedDelay 这个是等你方法执行完后,我再隔几秒来执行,也就是相对延迟后,以固定的频率去执行
# 有返回值的线程。通过Callable和Future创建线程
public static void main(String[] args) throws ExecutionException {
//Callable的返回值就要使用Future对象,Callable负责计算结果,Future负责拿到结果
//1、实现Callable接口
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
int i=999;
//do something
// eg request http server and process
return i;
}
};
//2、使用FutureTask启动线程
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
//3、获取线程的结果
try {
Thread.sleep(5000);// 可能做一些事情
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
# 线程池
public static void testFixedThreadPool() {
//创建固定的线程池,使用3个线程来并发执行提交的任务。底层是个无界队列
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(new MyThread());
executorService.execute(new MyRunnable());
executorService.execute(new MyRunnable());
executorService.execute(new MyRunnable());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
}
public static void testSingleThreadPool() {
//创建单线程,在任务执行时,会依次执行任务。底层是个无界队列。
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new MyThread());
executorService.execute(new MyRunnable());
executorService.execute(new MyRunnable());
executorService.execute(new MyRunnable());
executorService.execute(new MyThread());
}
public static void testCacheThreadPool() {
//创建非固定数量,可缓存的线程池。当提交的任务数量起起伏伏时,会自动创建或者减少执行线程的数量。
//当然,重用线程是线程池的基本特征。
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new MyThread());
executorService.execute(new MyRunnable());
executorService.execute(new MyRunnable());
executorService.execute(new MyRunnable());
executorService.execute(new MyThread());
}
public static void testScheduledThreadPool(){
//创建一个定时执行线程池
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(30);
//1、配置任务的执行周期
//scheduleAtFixedRate 固定周期执行完毕
executorService.scheduleAtFixedRate(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS);
//scheduleWithFixedDelay 上一次执行完毕之后下一次开始执行
executorService.scheduleWithFixedDelay(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS);
}
public static void testSingleCacheThreadPool(){
//创建一个单个线程执行的定时器
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
//scheduleAtFixedRate 固定周期执行完毕
executorService.scheduleAtFixedRate(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS);
//scheduleWithFixedDelay 上一次执行完毕之后下一次开始执行
executorService.scheduleWithFixedDelay(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS);
}
public static void testMyThreadPool(){
//自定义连接池稍微麻烦些,不过通过创建的ThreadPoolExecutor线程池对象,可以获取到当前线程池的尺寸、正在执行任务的线程数、工作队列等等。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,100,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
threadPoolExecutor.execute(new MyThread());
threadPoolExecutor.execute(new MyRunnable());
threadPoolExecutor.execute(new MyRunnable());
}
- Q:多线程中run()和start()的区别?
- run():仅仅是封装被线程执行的代码,直接调用是普通方法
- start():首先启动了线程,然后再由JVM去调用该线程的run()方法
- 线程调度
- 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
- 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
- Java使用的是抢占式调度模型。
- 线程的生命周期
- 新建:创建线程对象
- 就绪:有执行资格,没有执行权
- 执行:有执行资格,有执行权
- 阻塞:由于一些操作让线程处于了该状态。没有执行资格,没有执行权。而另一些操作却可以把它激活,激活后处于就绪状态。
- 死亡:线程对象变成垃圾,等待回收。
- 多线程安全问题
- 原因:是否是多线程环境;是否有共享数据 ;是否有多条语句操作共享数据。
- 解决方式:加锁
- 把多条语句操作共享数据的代码包成一个整体,让某个线程在执行的时候,别人不能来执行。
- 同步代码块:synchronized(对象){ code }
- 对象:可以是任意对象
- private Object obj = new Object();(成员变量,共用一把锁)
- 同步方法:锁对象为this
- JDK5之后提供lock锁
- lock():获取锁 unlock():释放锁。
- Q:如何把一个线程不安全的集合类变成一个线程安全的集合类?
- Collections工具类的带synchronized的方法
- Q:死锁问题
- 同步的弊端:效率低;容易产生死锁
- 死锁:两个或两个以上的线程在争夺资源过程中,发生的一种相互等待的现象。
最后
以上就是闪闪毛衣为你收集整理的Java高并发编程——多线程线程简介多线程实现方式的全部内容,希望文章能够帮你解决Java高并发编程——多线程线程简介多线程实现方式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复