Java 多线程与并发【知识点笔记】
Java 多线程与并发【知识点笔记】
Java多线程与并发
先说一下线程与进程的由来:
在初期的计算机,计算机只能串行执行任务,并且需要长时间的等待用户的输入才行
到了后来,出现了批处理,可以预先将用户的指令集中成清单,然后批量串行处理用户指令,但是这仍然无法并发执行
然后进程就出现了,进程独占内存空间,保存各自运行状态,相互间不干扰且可以互相切换,为并发处理任务提供了可能,这就解决了无法并发的情况
但是因为一个进程一段时间只做一个事情,如果一个进程有多个任务,只能一个一个的处理,甚是麻烦,因为就出现了线程,一个进程就包括了多个线程,线程可以共享进程的内存资源,相互之间切换更加的快速,支持更细粒度的任务控制,是进程内的子任务得到并发执行
进程和线程的区别
首先我们清楚,进程是资源分配的最小单位,线程是CPU调度的最小单位,而所有的与进程相关的资源都被记录在PCB中

而且,进程是抢占处理机的调度单位,线程属于某个进程,共享其资源,而线程只由堆栈寄存器,程序计数器和TCB组成

那么总的来说,这两个的区别有下面几点:
1.线程不能看做是一个独立的应用,而进程可以看做独立应用
2.进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径,线程没有独立的地址空间,多进程的程序比多线程程序健壮
3.进程的切换比线程的切换开销更大
在Java中进程和线程的关系
1.Java作为与平台无关的编程语言,就会对操作系统提供的功能进行封装,包括进程和线程
2.每运行一个Java程序就会产生一个进程,而进程包含至少一个线程
3.每个进程对应一个JVM实例,多个线程共享JVM里的堆
4.进程类似于投资者,其手中掌握着资源,拿着资源去干活打工的就是线程,而Java采用的是单线程编程模型,程序会自动创建主线程
5.主线程可以创建子线程,原则上要后于子线程完成执行
Thread中的start和run方法的区别

调用start()方法会创建一个新的子线程启动,而run()方法只是thread的一个普通方法的调用
Thread和runnable的关系
Thread是一个类,而runnable是一个接口,Thread是一个可以实现runnable接口的类,可以让runnable支持多线程
因为Java类的单一继承原则,为了提升系统的可扩展性,推荐使业务类实现runnable接口,将业务逻辑分装在run方法里,便于给普通的类附上多线程的特性
怎么给run()方法传参?
实现的方法主要有三种:构造函数传参,成员变量传参以及回调函数传参
如何实现处理线程的返回值?
实现的方式主要有三种:
第一种方法,主线程等待法,实现起来比较简单,缺点就是需要自己实现循环等待的逻辑,当需要等待的变量多起来,代码就会看起来很臃肿
代码如下:
public class CycleWait implements Runnable{
private String value;
public void run() {
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "we have data now";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
t.start();
while (cw.value == null){
Thread.currentThread().sleep(100);
}
System.out.println("value : " + cw.value);
}
}
第二种方法,使用Thread类的join()阻塞当前线程以等待子线程处理完毕,这种能够比主线程等待法的控制更加精准,实现起来也更简单,但是缺点是粒度不够细
代码不同之处就是
public static void main(String[] args) throws InterruptedException {
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
t.start();
t.join();
System.out.println("value : " + cw.value);
}
}
第三种方法,通过callable接口实现:通过futuretask 或者是通过线程池获取,可以更加精准的进行控制
其中的一种代码如下:
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
Future<String> future = newCachedThreadPool.submit(new MyCallable());
if(!future.isDone()){
System.out.println("task has not finished, please wait!");
}
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
newCachedThreadPool.shutdown();
}
}
}
线程的状态
线程主要有六个状态:
新建(new):创建以后尚未启动的线程的状态
运行(runnable):包含running和ready,有可能正在执行,也可能在等待
无限期等待(waiting):不会被分配CPU执行时间,需要显式被唤醒,通过三个方法可以进入无限期等待:调用了没有设置timeout参数的object.wait()方法或者是没有设置timeout参数的thread.join()方法以及locksupport.park()方法
限期等待(timed waiting):在一定时间后会被系统自动唤醒,主要有五个方法可以进入限期等待:第一个是thread.sleep()方法,第二个是设置了设置timeout参数的object.wait()方法,第三个是设置timeout参数的thread.join()方法,第四个是locksupport.parknanos()方法,第五个是locksupport.parkuntil()方法
阻塞(blocked):等待获取排它锁
结束(terminated):已终止线程的状态,线程已经结束执行
线程状态以及状态之间的转换

sleep和wait的区别
基本的差别
sleep是thread类的方法,wait是object类中定义的方法
sleep方法可以在任何地方使用,而wait方法只能在synchronized方法或者是synchronized块中使用
最主要的本质上的区别
Thread.sleep只会让出CPU,不会导致锁行为的改变,而object.wait不仅让出CPU,还会释放已经占有的同步资源锁
notify和notify all的区别
要想先了解这两个的区别,就得先知道两个概念:锁池entrylist以及等待池waitset
锁池的本质可以这样说,假设线程a已经拥有了某个对象的锁,而其他的线程b和c想要调用这个对象的某个synchronized方法或者是synchronized块,由于b和c线程在进入对象的synchronized方法(或者是块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正在被线程a占用,此时线程b和c就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池
等待池的本质可以这样说,假设线程a调用了某个对象的wait方法,线程a就会释放该对象的锁,同时线程a就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁
然后就可以说一下notify和notifyall的区别了
notifyall会让所有处于等待池中的线程全部进入锁池去竞争获取锁的机会,没有获得锁的只能在所持中等待下个机会,不能再回到等待池中
而notify只会随机取一个处于等待池中的线程进入锁池中去竞争获取锁的机会
yield函数
概念部分:当调用thread.yield()函数的时候,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示
示例代码如下:
public class YieldDemo {
public static void main(String[] args) {
Runnable yieldTask = new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
if (i == 5) {
Thread.yield();
}
}
}
};
Thread t1 = new Thread(yieldTask, "A");
Thread t2 = new Thread(yieldTask, "B");
t1.start();
t2.start();
}
如何中断线程?
已经被抛弃的方法:通过调用stop()方法停止线程(因为太过暴力等问题被停止使用)以及通过调用suspend()和resume()方法
目前在使用的方法:调用interrupt()方法,作用不是中断线程,而是通知线程应该中断了,具体来说,就是如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个interruptedexception异常,那么如果线程处于正常的活动状态,那么会将该线程的中断标志设置为true,被设置中断标志的线程将继续正常运行,不受影响
因此interrupt并不能真正的中断线程,而是需要被中断的线程来配合中断才可以,也就是说,一个线程有了被中断的需求才能这样做,在正常运行任务的时候,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程,如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标志的线程将继续正常运行,不受影响
示例代码如下:
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Runnable interruptTask = new Runnable() {
@Override
public void run() {
int i = 0;
try {
//在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(100); // 休眠100ms
i++;
System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") loop " + i);
}
} catch (InterruptedException e) {
//在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程)
System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") catch InterruptedException.");
}
}
};
Thread t1 = new Thread(interruptTask, "t1");
System.out.println(t1.getName() +" ("+t1.getState()+") is new.");
t1.start(); // 启动“线程t1”
System.out.println(t1.getName() +" ("+t1.getState()+") is started.");
// 主线程休眠300ms,然后主线程给t1发“中断”指令
Thread.sleep(300);
t1.interrupt();
System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
// 主线程休眠300ms,然后查看t1的状态
Thread.sleep(300);
System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
}
}

Java 多线程与并发【知识点笔记】的更多相关文章
- Java 多线程高并发编程 笔记(一)
本篇文章主要是总结Java多线程/高并发编程的知识点,由浅入深,仅作自己的学习笔记,部分侵删. 一 . 基础知识点 1. 进程于线程的概念 2.线程创建的两种方式 注:public void run( ...
- JAVA 多线程和并发学习笔记(三)
Java并发编程中使用Executors类创建和管理线程的用法 1.类 Executors Executors类可以看做一个“工具类”.援引JDK1.6 API中的介绍: 此包中所定义的 Execut ...
- JAVA 多线程和并发学习笔记(二)
一.Java中创建线程方法 1. 继承Thread类创建线程类 定义Thread类的子类,重写该类的run()方法.该方法为线程执行体. 创建Thread子类的实例.即线程对象. 调用线程对象的sta ...
- JAVA 多线程和并发学习笔记(四)
1. 多进程 实现并发最直接的方式是在操作系统级别使用进程,进程是运行在它自己的地址空间内的自包容的程序.多任务操作系统可以通过周期性地将CPU从一个进程切换到另一个进程,来实现同时运行多个进程. 尽 ...
- Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition
锁的概念 从jdk发行1.5版本之后,在原来synchronize的基础上,增加了重入锁ReentrantLock. 本文就不介绍synchronize了,有兴趣的同学可以去了解一下,本文重点介绍Re ...
- Java多线程高并发学习笔记(一)——Thread&Runnable
进程与线程 首先来看百度百科关于进程的介绍: 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动.它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体.它不只是程序的代码,还包括当前的 ...
- Java多线程高并发学习笔记——阻塞队列
在探讨可重入锁之后,接下来学习阻塞队列,这边篇文章也是断断续续的写了很久,因为最近开始学ssm框架,准备做一个自己的小网站,后续可能更新自己写网站的技术分享. 请尊重作者劳动成果,转载请标明原文链接: ...
- Java 多线程高并发编程 笔记(二)
1. 单例模式(在内存之中永远只有一个对象) 1.1 多线程安全单例模式——不使用同步锁 public class Singleton { private static Singleton sin=n ...
- JAVA 多线程和并发学习笔记(一)
一.进程与线程 1. 进程 当一个程序进入内存运行时,即变成一个进程.进程是处于运行过程中的程序.进程是操作系统进行资源分配和调度的一个独立单位.进程的三个特征: 独立性 独立存在的实体,每个进程都有 ...
- JAVA多线程高并发学习笔记(三)——Callable、Future和FutureTask
为什么要是用Callable和Future Runnable的局限性 Executor采用Runnable作为基本的表达形式,虽然Runnable的run方法能够写入日志,写入文件,写入数据库等操作, ...
随机推荐
- uni-app中组件picker的基本使用(日期选择器为例)
例:需要在下图"自定义日期"中使用日期选择器 <template> <div> <picker mode="date" @chan ...
- Docker:Docker常用命令
docker信息 ##查看docker容器版本 docker version ##查看docker容器信息 docker info ##查看docker容器帮助 docker --help 镜像列表 ...
- 机器学习Sklearn系列:(三)决策树
决策树 熵的定义 如果一个随机变量X的可能取值为X={x1,x2,..,xk},其概率分布为P(X=x)=pi(i=1,2,...,n),则随机变量X的熵定义为\(H(x) = -\sum{p(x)l ...
- python04篇 文件操作(二)、集合
一.文件操作(二) 1.1 利用with来打开文件 # with open ,python 会自动关闭文件 with open('a.txt', encoding='utf-8') as f: # f ...
- shell编程之循环语句for / while / until
shell编程之循环语句与函数 一.条件测试 二.循环语句 ① for循环语句结构(遍历) 示例1 示例2 ② while循环语句结构(迭代) 示例1 示例2 ③ until 循环语句结构 示例1 一 ...
- 家庭账本开发day07
返回数据问题解决,需要按照规定的json数据进行返回. 利用jsonobejact或者GSON工具将对象ArrayList转化为json 格式.然后response.getWriter().write ...
- MapReduce显示最受欢迎的Top10课程(按照课程编号)
上篇博客已经说过,会将代码进行优化,并通过TreeMap进行排序实现,现在简单说明一下代码的思路. 项目以上传到github:https://github.com/yandashan/MapReduc ...
- Java基础00-集合进阶26
1. Collection Collection常用方法详解 1.1 集合知识回顾 1.2 集合类体系结构 集合存储数据的方式有单列和双列 Collection中还有List(可以存储可重复的数据)和 ...
- Collection集合工具类
Ⅷ.Collections 工具类 java.util.Collections Collections 集合工具类,用来对集合进行操作,部分重要方法如下: 1.public static <T& ...
- 前端开发入门到进阶第五集【安装SublimeServer】
参考:https://www.cnblogs.com/jf-67/p/8031614.html 1.我们可以直接在sublime text里面安装,Ctrl+shift+p进入命令模式,输入insta ...