【Java】 Java多线程(一)
一.对线程的理解
1.线程概念
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
2.线程的创建方式
1、继承java.lang.Thread类,并且重写它的run()方法,将线程的执行主体放在其中;
2、实现java.lang.Runnable接口,实现它的run()方法,并将线程的执行主体放在其中;
3、实现Callable接口,并实现它的call()方法实现多线程(JDK1.5)
(由于Java中类是单继承的,所以当类继承一个类时,就无法使用方式一了,开发中方式二更常用)
3.线程的状态
4.线程的同步
线程同步方式一(synchronized关键字)
可以同步方法,也可以同步代码块;对于同步方法来说,每个方法只有获取到所属类实例的锁才可以被执行,一旦该方法被执行,则独占锁,知道方法返回时或者异常退出时才会释放掉锁;同步代码块也是一样,当两个并发线程访问同一个对象中的这个synchronized(this)代码块的时候,一个时间内只有一个线程得到执行,另一个线程只有在这个线程执行完成之后才可以执行;
线程同步方式一(Lock机制)
Lock是一个接口,它是jdk1.5新增的,实现Lock接口类具有与synchronized关键字相同的功能,但功能更加强大java.utils.concurrent.locks.ReentrantLock是比较常用的;注意需要在finally中unlock释放锁;
线程阻塞
sleep()方法、yield()方法、wait()和join()等方法都可以使线程进入阻塞状态;但是yield方法和wait方法都会释放锁(cpu运行时间),而sleep方法不会释放锁。
5.线程与进程
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP(多核处理机)机器上运行,而进程则可以跨机器迁移。
二.对多线程的理解
为什么要有多线程
多线程的出现是为了提高程序的运行效率。(但并不代表多线程的程序运行效率一定高)
多线程调度
线程可以完成一定的任务,可以与其他线程共享父进程中的共享变量及部分环境,相互之间协同来完成进程所要完成的任务。线程是独立运行的,它并不知道进程中是否还有其他线程存在,线程的执行是抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行。
多线程的安全问题
多个线程在抢占执行时可能会发生安全问题 (死锁,活锁,饿锁)
1.死锁应该是最糟糕的一种情况了,它表示两个或者两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
2.活锁则是类似于: 线程A和B都需要过桥(都需要使用某个资源),而都礼让不走,就这么僵持下去的情况.
3.饿锁则类似于: 线程A想要某个资源,但后面不停的有线程拿走那个资源,从而A一直拿不到那个资源的情况
多线程产生死锁的必要条件
互斥条件:一个资源每次只能被一个线程使用
请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺
循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
死锁案例
package test;
public class DeadLock{
private static Object o1 = new Object(), o2 = new Object();//用作锁的两个对象
public static void main(String[] args)
{
new Thread(() -> {//lambda表达式
System.out.println("线程1开始执行");
synchronized (o1){
try{
System.out.println("线程1拿到o1锁");
Thread.sleep(1000);//线程休眠,让第二个线程有机会执行
}catch(Exception e){
e.printStackTrace();
}
synchronized (o2){
System.out.println("线程1拿到o2锁执行完毕");
}
}
}).start();
new Thread(() -> {
System.out.println("线程2开始执行");
synchronized (o2){
try{
System.out.println("线程2拿到o2锁");
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
synchronized (o1){
System.out.println("线程2拿到o1锁执行完毕");
}
}
}).start(); }
}
这两个线程形成死锁,谁都无法执行完毕。
避免死锁的方式
1.指定线程的执行顺序,例如,设置一个变量,每次一个执行线程完毕后变量值+1,执行下一个线程前判断变量的值。
2.“银行家”算法(资源数量 >= 线程数量*(每个线程需要的资源数量-1) + 1 时,不会出现死锁)
3.死锁检测:当一个线程获取锁的时候,会在相应的数据结构中记录下来,相同下,如果有线程请求锁,也会在相应的结构中记录下来。当一个线程请求失败时,需要遍历一下这个数据结构检查是否有死锁产生。例如:线程A请求锁住一个方法1,但是现在这个方法是线程B所有的,这时候线程A可以检查一下线程B是否已经请求了线程A当前所持有的锁,像是一个环,线程A拥有锁1,请求锁2,线程B拥有锁2,请求锁1。 当遍历这个存储结构的时候,如果发现了死锁,一个可行的办法就是释放所有的锁,回退,并且等待一段时间后再次尝试。
三.线程池的理解
线程池出现的原因
线程的频繁创建和销毁是很影响性能的一件事情。而使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。同时,根据系统的承受能力,调整线程池中工作线程的数量,防止浪费内存。
线程池类
java.util.concurrent.ThreadPoolExecutor 类就是一个线程池。客户端调用 ThreadPoolExecutor.submit(Runnable task) 提交任务,线程池内部维护的工作者线程的数量就是该线程池的线程池大小,有 3 种形态:
当前线程池大小 :表示线程池中实际工作者线程的数量;
最大线程池大小 (maxinumPoolSize):表示线程池中允许存在的工作者线程的数量上限;
核心线程大小 (corePoolSize ):表示一个不大于最大线程池大小的工作者线程数量上限。(如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;如果运行的线程等于或者多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不是添加新线程;如果无法将请求加入队列,即队列已经满了,则创建新的线程,除非创建此线程超出 maxinumPoolSize, 在这种情况下,任务将被拒绝)
线程池的使用
1.创建线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler);//参数根据情况设置
2.使用线程池中的线程
pool.execute(new Runnable(){
@override
public void run(){
//code
}
});
【Java】 Java多线程(一)的更多相关文章
- Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)
一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...
- java之多线程 二
线程的生命周期: 当线程被创建并被启动时,它既不是一启动就进入了执行状态,在线程的生命周期中,它要经过new(新建),就绪(Runnable),运行(Running),阻塞(Blocked),dead ...
- Java的多线程机制系列:(一)总述及基础概念
前言 这一系列多线程的文章,一方面是个人对Java现有的多线程机制的学习和记录,另一方面是希望能给不熟悉Java多线程机制.或有一定基础但理解还不够深的读者一个比较全面的介绍,旨在使读者对Java的多 ...
- Java Thread 多线程 介绍
1.线程概述 几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程. 当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程. 2.线程 ...
- Java:多线程<一>
程序运行时,其实是CPU在执行程序的进程,为了提高工作效率一个进程可以有多个线程. Java的多线程: 其实我们之前就见过Java的线程,main就是Java的一个线程,还有另一个条线程总是和main ...
- Java的多线程机制系列:(四)不得不提的volatile及指令重排序(happen-before)
一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...
- Java的多线程机制系列:(三)synchronized的同步原理
synchronized关键字是JDK5之实现锁(包括互斥性和可见性)的唯一途径(volatile关键字能保证可见性,但不能保证互斥性,详细参见后文关于vloatile的详述章节),其在字节码上编译为 ...
- Java基础——多线程
Java中多线程的应用是非常多的,我们在Java中又该如何去创建线程呢? http://www.jianshu.com/p/40d4c7aebd66 一.常用的有三种方法来创建多线程 新建一个类继承自 ...
- JAVA之多线程的创建
转载请注明源出处:http://www.cnblogs.com/lighten/p/5967853.html 1.概念 老调重弹,学习线程的时候总会牵扯到进程的概念,会对二者做一个区分.网上有较多的解 ...
- Java基础--多线程的方方面面
1,什么是线程?线程和进程的区别是什么? 2,什么是多线程?为什么设计多线程? 3,Java种多线程的实现方式是什么?有什么区别? 4,线程的状态控制有哪些方法? 5,线程安全.死锁和生产者--消费者 ...
随机推荐
- 出现org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER, 且出现无法找到Maven的依赖的问题
解决方案:Build Path -> Java Build Path ->Libraries ->Add Library ->Maven Managed Dependences ...
- IDEA配置常见配置
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- 【转】diamond专题(一)– 简介和快速使用
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- C与指针学习笔记
有些任务无法用其他语言实现,如直接访问硬件,但C却可以. C对数组下标引用和指针访问并不进行有效性检查,这可以节省时间,但你在使用这些特性时,就必须特别小心.
- spark 笔记 4:Apache Hadoop YARN: Yet Another Resource Negotiator
spark支持YARN做资源调度器,所以YARN的原理还是应该知道的:http://www.socc2013.org/home/program/a5-vavilapalli.pdf 但总体来说, ...
- 一、基础篇--1.1Java基础-Session和Cookie的区别【转】
https://www.cnblogs.com/zlw-xf/p/8001383.html 1:cookie数据存放在客户的浏览器上(客户端),session数据放 @1:cookie不是很安全,别人 ...
- 全面解读php-面向对象
一.类的自动载入 //类的自动载入我们使用 spl_autoload_register($autoload_function ).我们需要在不同的地方包含更多不同的类文件,只需要多写几个 $autol ...
- [转]Tomcat中8005/8009/8080/8443端口的作用
8005:关闭tomcat进程所用.当执行shutdown.sh关闭tomcat时就是连接8005端口执行“SHUTDOWN”命令--由此,我们直接telnet8005端口执行“SHUTDOWN”(要 ...
- matplotlib之折线图
1.案例一 # coding=utf-8 from matplotlib import pyplot as plt import random # 设置字体相关 from matplotlib imp ...
- 一键发布shell脚本
1.配置集群间免密登录 (1)配置host:vim /etc/hosts (2)生成公钥 :ssh-keygen -t rsa -P '' 这时会提示生成的公钥的存放地址,直接回车,公钥生成成功 (3 ...