一、 什么是进程、线程?线程和进程的区别?
1. 进程
当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序。
进程是操作系统进行资源分配和调度的一个独立单位。
进程的三个特征:

独立性
独立存在的实体,每个进程都有自己独立私有的一块内存空间。
动态性
程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
并发性
多个进程可在单处理器上并发执行。
并发性和并行性
并发是指在同一时间点只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
并行指在同一时间点,有多条指令在多个处理器上同时执行。
1
2
3
2. 线程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程也被称作轻量级进程。线程在进程中是独立,并发的执行流。
3.线程和进程的区别

线程是进程的组成部分,一个进程可以有很多线程,每条线程并行执行不同的任务。
不同的进程使用不同的内存空间,而线程与父进程的其他线程共享父进程的所拥有的全部资源。这样编程方便了,但是要更加小心。
别把内存空间和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。线程拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源。
线程的调度和管理由进程本身负责完成。操作系统对进程进行调度,管理和资源分配。
二、 多线程的优势
进程之间不能共享内存,但线程之间共享内存。
系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小很多,效率高。
资源利用率更好
程序设计更简单
程序响应更快
后三条详细见http://ifeve.com/benefits/
三、 Java中创建线程方法
1. 继承Thread类创建线程类

定义Thread类的子类,重写该类的run()方法。该方法为线程执行体。
创建Thread子类的实例。即线程对象。
调用线程对象的start()方法启动该线程
2. 实现Runnable接口创建线程类
定义Runnable接口的实现类,重写该接口的run()方法。该方法为线程执行体。
创建Runnable实现类的实例。并以此实例作为Thread的target来创建Thread对象。该Thread对象才是真正的线程对象。
调用线程对象(该Thread对象)的start()方法启动该线程。
3. 使用Callable和Future创建线程
http://blog.csdn.net/ghsau/article/details/7451464
四、 用Runnable还是ThreadJava以及创建线程两种方法对比?
在java多线程中,一般推荐采用实现Runnable接口来创建多线程,因为实现Runnable接口相比继承Thread类有如下优劣势:

实现Runnable接口,线程类只是实现了接口,还可以继承其他类;继承Thread类的话,不能再继承其他父类。
实现Runnable接口,多个线程可以共享同一个target对象,所以适合多个相同程序代码的线程区处理同一资源的情况。分离数据和代码,体现面向对象的思想。
实现Runnable接口,访问当前线程,必须使用Thread.currentThread()方法;继承Thread类的话,使用this获得当前线程。
与http://blog.csdn.net/ns_code/article/details/17161237互补。
五、 Thread 类中的start() 和 run() 方法有什么区别?
start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。
当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。
需要特别注意的是:不能对同一线程对象两次调用start()方法。
六、 线程的生命周期
Java线程五种状态:

新建状态(New):当线程对象创建后,即进入了新建状态。仅仅由java虚拟机分配内存,并初始化。如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,java虚拟机创建方法调用栈和程序计数器,只是说明此线程已经做好了准备,随时等待CPU调度执行,此线程并 没有执行。
运行状态(Running):当CPU开始调度处于就绪状态的线程时,执行run()方法,此时线程才得以真正执行,即进入到运行状态。注:绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态,JVM会把该线程放入等待池中;
同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程run()方法执行完了或者因异常退出了run()方法,该线程结束生命周期。
当主线程结束时,其他线程不受任何影响。
七、 java控制线程方法
1. join线程
join方法用线程对象调用,如果在一个线程A中调用另一个线程B的join方法,线程A将会等待线程B执行完毕后再执行。
2. 守护线程(Daemon Thread)
Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 。
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。
守护线程的特征:如果所有前台线程都死亡,后台线程会自动死亡。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。
虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。
另外有几点需要注意:

1、setDaemon(true)必须在调用线程的start()方法之前设置,否则会跑出IllegalThreadStateException异常。
2、在守护线程中产生的新线程也是守护线程。
3、 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。
1
2
3
参考http://blog.csdn.net/ns_code/article/details/17099981
3. 线程让步(yield )
yield可以直接用Thread类调用,可以让当前正在执行的线程暂停,不会阻塞该线程,只是将该线程转入就绪状态。yield让出CPU执行权给同等级的线程,如果没有相同级别的线程在等待CPU的执行权,则该线程继续执行。

八、 sleep()方法和yield()方法的区别
sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会。
sleep()方法会将线程转入阻塞状态(block状态),直到经过阻塞时间才会转入就绪状态;而yield()方法不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。 因此完全有可能某个线程调用yield()方法暂停之后,立即再次获得处理器资源被执行。
sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常。
sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。
九、 为什么Thread类的sleep()和yield()方法是静态的?
Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。
现在的实现, 是只能sleep当前的线程.当前线程是自愿的.让sleep()成为实例方法, 当前线程可以直接sleep别的线程, 会引入很多 多线程问题,例如死锁。
destroy(), suspend(), stop(),resume()这些实例方法都已经被deprecated(弃用)。活下来的是哪些? 只有static方法(只对当前线程操作和一些比较温和的实例方法, 如getXXX(), isXXX(), join(), yield()等.

十、 sleep方法与wait方法的区别?
sleep方法是静态方法,wait方法是非静态方法。
sleep方法在时间到后会自己“醒来”,但wait不能,必须由其它线程通过notify(All)方法让它“醒来”。
sleep方法通常用在不需要等待资源情况下的阻塞,像等待线程、数据库连接的情况一般用wait。
十一、 线程安全问题
线程安全问题,其实是指多线程环境下对共享资源的访问可能会引起此共享资源的不一致性。因此,为避免线程安全问题,应该避免多线程环境下对此共享资源的并发访问。

十二、 同步代码块
同步代码块的格式为:

synchronized (obj) {
//...
}
1
2
3
4
其中,obj为锁对象,因此,选择哪一个对象作为锁是至关重要的。一般情况下,都是选择此共享资源对象作为锁对象。
任何时刻只能有一个线程可以获得对锁对象的锁定,其他线程无法获得锁,也无法修改它。当同步代码块执行完成后,该线程会释放对锁对象的锁定。
通过这种方式可以保证并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(临界区),从而保证线程的安全性。

十三、 同步方法
对共享资源进行访问的方法定义中加上synchronized关键字修饰,使得此方法称为同步方法。可以简单理解成对此方法进行了加锁,其锁对象为当前方法所在的对象自身。多线程环境下,当执行此方法时,首先都要获得此同步锁(且同时最多只有一个线程能够获得),只有当线程执行完此同步方法后,才会释放锁对象,其他的线程才有可能获取此同步锁,以此类推…

public synchronized void a() {
// ....
}
1
2
3
4
可变类的线程安全是以降低程序的运行效率为代价的,为了减少程序安全所带来的负面影响,程序可以采用如下策略:
- 不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法进行同步。
- 如果可变类有两种运行环境:单线程和多线程环境,则应该为该可变类提供两种版本:线程不安全版本和线程安全版本。在单线程环境中使用线程不安全版本以保证性能,在多线程环境中使用线程安全版本。

十四、 何时会释放对同步监视器锁定?
程序无法显式的释放对同步监视器的锁定,线程可以通过以下方式释放锁定:
A、当线程的同步方法、同步代码库执行结束,就可以释放同步监视器
B、当线程在同步代码库、方法中遇到break、return终止代码的运行,也可释放
C、当线程在同步代码库、同步方法中遇到未处理的Error、Exception,导致该代码结束也可释放同步监视器
D、当线程在同步代码库、同步方法中,程序执行了同步监视器对象的wait方法,导致方法暂停,释放同步监视器

下面情况不会释放同步监视器:
A、当线程在执行同步代码库、同步方法时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前程序,当前程序不会释放同步监视器
B、当线程在执行同步代码库、同步方法时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。注意尽量避免使用suspend、resume
1
2
3
十五、同步锁(Lock)
通常认为:Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock更灵活的结构,有很大的差别,并且可以支持多个Condition对象
Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,
线程开始访问共享资源之前应先获得Lock对象。不过某些锁支持共享资源的并发访问,如:ReadWriteLock(读写锁),在线程安全控制中,
通常使用ReentrantLock(可重入锁)。使用该Lock对象可以显示加锁、释放锁。

class C {
//锁对象
private final ReentrantLock lock = new ReentrantLock();
......
//保证线程安全方法
public void method() {
//上锁
lock.lock();
try {
//保证线程安全操作代码
} catch() {

} finally {
lock.unlock();//释放锁
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用Lock对象进行同步时,锁定和释放锁时注意把释放锁放在finally中保证一定能够执行。使用锁和使用同步很类似,只是使用Lock时显示的调用lock方法来同步。而使用同步方法synchronized时系统会隐式使用当前对象作为同步监视器,同样都是“加锁->访问->释放锁”的操作模式,都可以保证只能有一个线程操作资源。
同步方法和同步代码块使用与竞争资源相关的、隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且获得多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放所有资源。
Lock提供了同步方法和同步代码库没有的其他功能,包括用于非块结构的tryLock方法,已经试图获取可中断锁lockInterruptibly()方法, 还有获取超时失效锁的tryLock(long, timeUnit)方法。
ReentrantLock具有重入性,也就是说线程可以对它已经加锁的ReentrantLock再次加锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,线程在每次调用lock()加锁后,必须显示的调用unlock()来释放锁,所以一段被保护的代码可以调用另一个被相同锁保护的方法。

十六、死锁
当2个线程相互等待对方是否同步监视器时就会发生死锁,JVM没有采取处理死锁的措施,这需要我们自己处理或避免死锁。
一旦死锁,整个程序既不会出现异常,也不会出现错误和提示,只是线程将处于阻塞状态,无法继续。
由于Thread类的suspend也很容易导致死锁,所以Java不推荐使用此方法暂停线程。
参考http://ifeve.com/deadlock/了解更多死锁情况。
大部分代码并不容易产生死锁,死锁可能在代码中隐藏相当长的时间,等待不常见的条件地发生,但即使是很小的概率,一旦发生,便可能造成毁灭性的破坏。避免死锁是一件困难的事,遵循以下原则有助于规避死锁:

1、只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;

2、尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;

3、创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;
————————————————

JAVA多线程和并发性知识点总结的更多相关文章

  1. Java 多线程与并发【知识点笔记】

    Java 多线程与并发[知识点笔记] Java多线程与并发 先说一下线程与进程的由来: 在初期的计算机,计算机只能串行执行任务,并且需要长时间的等待用户的输入才行 到了后来,出现了批处理,可以预先将用 ...

  2. Java多线程与并发模型之锁

    这是一篇总结Java多线程开发的长文.文章是从Java创建之初就存在的synchronized关键字引入,对Java多线程和并发模型进行了探讨.希望通过此篇内容的解读能帮助Java开发者更好的理清Ja ...

  3. Java 多线程高并发编程 笔记(一)

    本篇文章主要是总结Java多线程/高并发编程的知识点,由浅入深,仅作自己的学习笔记,部分侵删. 一 . 基础知识点 1. 进程于线程的概念 2.线程创建的两种方式 注:public void run( ...

  4. 031.[转] 从类状态看Java多线程安全并发

    从类状态看Java多线程安全并发 pphh发布于2018年9月16日 对于Java开发人员来说,i++的并发不安全是人所共知,但是它真的有那么不安全么? 在开发Java代码时,如何能够避免多线程并发出 ...

  5. Java 多线程与并发【原理第一部分笔记】

    Java 多线程与并发[原理第一部分笔记] Synchronized synchronized的基本含义以及使用方式 在Java中线程安全问题的主要诱因就是存在共享数据(也称为临界资源)以及存在多条线 ...

  6. JAVA 多线程和并发学习笔记(三)

    Java并发编程中使用Executors类创建和管理线程的用法 1.类 Executors Executors类可以看做一个“工具类”.援引JDK1.6 API中的介绍: 此包中所定义的 Execut ...

  7. JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  8. [转] JAVA多线程和并发基础面试问答

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  9. JAVA多线程和并发基础面试问答

    转载: JAVA多线程和并发基础面试问答 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对 ...

  10. 【多线程】JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

随机推荐

  1. jenkins集成cucumber-resport报告

    需要安装的jenkins插件-Cucumber Reports jenkins版本:2.273 jenkins插件下载地址:点击下载 下载插件后通过jenkins插件管理上传已下载好的插件即可 等待j ...

  2. windows下git安装以及基本使用

    Git的基本使用 Windows下Git的基本使用与操作 安装Git 这个是一个git的windows系统的命令行版本 https://git-scm.com/downloads 安装 tortois ...

  3. token、cookie、session的区别

    token是验证用户身份的令牌,通常是用于接口的身份验证,一般获取之后放在请求头中 cookie和session的区别: cookie是服务器发送到客户端然后存储在客户端的,可以用来保持用户的登录信息 ...

  4. json编码格式化美化

    有时候你想存储一个json到文件中,然后让别人调用或者读取或者作为临时存储,诸如此类. 但是php json_encode后数据是压缩的没有格式化,导致读起来有点费劲. 所以你可以这样(php 5.4 ...

  5. Linux中tar文件压缩与解压

    文件压缩与解压缩 一般什么情况下使用文件压缩? 备份数据,数据传输 节省磁盘空间 减少带宽使用 减少负载 减少IO操作 什么情况下进行压缩比较合适? 错过业务高峰期,由于文件的压缩会瞬间加大cpu的负 ...

  6. Blazor 组件库 BootstrapBlazor 中Alert组件介绍

    组件介绍 Alert组件几乎是组件库里必不可少的组件了,即使浏览器,也自带了一个alert,会弹出一个模态框. 但是这个模态框有点太丑了,所以各大组件库分分实现了自己的Alert组件. 当然Boots ...

  7. 利用 word VBA 将投标文件偏离参数表列数据拷贝至技术偏差表中

    使用 vba 将正偏离参数表的第一列信息复制粘贴至对应的技术偏离表的第4列中.需要同时打开两个 word 文件,在技术偏差表中打开 VBE(可以用ctrl + f11 快捷键),插入模块. 忽略格式的 ...

  8. Ubuntu使程序脱离终端运行

    应用场景: 远程登陆Linux服务器运行模型训练代码,如果关闭本地终端则服务器代码中断运行!目标操作:在本地终端运行服务器代码,当关闭终端时代码能够继续在服务器上运行,且再次打开终端连接服务器时能找回 ...

  9. DataGridView频繁更新PLC报警信息数据源

    1.问题描述 当DataGridView频繁更新数据源时,可能会导致界面闪烁.性能下降等问题.这是因为每次更新数据源时,DataGridView都需要重新绘制和绑定数据,这是一个相对耗时的过程. 2. ...

  10. codeforces 1978 D. Elections

    题目链接 https://codeforces.com/problemset/problem/1978/D 题意 对于每个测试用例,共有 \(n\) 个人,每个人的号码分别是 \(1,2,...,n\ ...