JAVA并行程序基础
JAVA并行程序基础
一、有关线程你必须知道的事
进程与线程
- 在等待面向线程设计的计算机结构中,进程是线程的容器。我们都知道,程序是对于指令、数据及其组织形式的描述,而进程是程序的实体。
- 线程是轻量级的进程,是程序执行的最小单位。(PS:使用多线程去进行并发程序的设计,是因为线程间的调度和切换成本远小于进程)
线程的状态(Thread的State类):
- NEW–刚刚创建的线程,需要调用start()方法来执行线程;
- RUNNABLE–线程处于执行状态;
- BLOCKED–线程遇到synchronized同步块,会暂停执行直到获得请求的锁;
- WAITING–无限制时间的等待;
- TIMED_WAITING–有限制时间的等待;
- TERMINATED–执行完毕,表示结束。
二、线程的基本操作
创建线程
代码示例:start()方法会新建线程并调用run()方法!
Thread t = new Thread();
t.start();
注意以下代码:这段代码也可以编译执行,但不能新建一个线程,而是在当前线程中调用run()方法。(PS:就是作为一个普通方法来调用)
Thread t = new Thread();
t.run();
不能用run()来开启线程,它只会在当前线程中串行执行run()方法。
另外,Thread类有一个非常重要的构造方法:
public Thread(Runnable target)
这个构造方法在start()调用时,新线程会执行Runnable.run()方法。实际上,默认的Thread.run()也是这样子做的。
public class MyThread implements Runnable {
public static void main(String[] args) {
Thread t = new Thread(new MyThread());
t.start();
} @Override
public void run() {
System.out.println("开启新线程执行run()方法");
}
}
- 继承Thread的方式定义线程后,就不能在继承其他的类了,导致程序的可扩展性大大降低。而且**重载**run()方法也只是和普通方法一样,不会被JVM主动调用。
- 使用实现Runnable接口并传入实例给Thread,可以避免重载Thread.run(),单纯使用接口来定义Thread。
终止线程
JAVA提供了一个stop()方法来关闭线程,不过这是一个被标注为废弃的方法。原因是stop()方法太过暴力,强行将执行到一半的线程关闭可能导致一些数据不一致的问题。
一帮情况下,不要使用 stop() 方法!
解决方法:定义一个标记变量stopme
,用于指示线程是否需要退出。
线程中断
在JAVA中,为了完善线程安全退出,有一种重要的线程协作机制————线程中断。(注意:线程中断会给线程发送一个通知,但线程接到通知后如何处理,则由目标线程自行决定)
- 线程中断的三个重要方法
public void Thread.interrupt() //中断线程
public boolean Thread.isInterrupted() //判断线程是否被中断
public static boolean Thread.interrupted() //判断线程是否被中断,并清除当前中断状态
public static boolean Thread.interrupted() //判断线程是否被中断,并清除当前中断状态
- 1
- 2
- 3
Thread.interrupt()
方法中断,线程不会立刻停下来,比如死循环体。可以通过isInterrupted()
方法来判断是否跳出循环体。
线程休眠
Thread.sleep()
让当前线程休眠若干时间,其签名如下:
public static native void sleep(long millis) throws InterruptedException
- InterruptedException不是运行时异常,当线程处于休眠状态时,如果被中断就会抛出这个异常。
- sleep()方法由于中断抛出异常时,会清除中断标记。所以,在异常处理中需要再次设置中断标记!
Thread.wait()与notify()
这两个方法是配套的,一个线程调用obj.wait()
处于等待状态,需要其他线程调用obj.notify()
来唤醒。obj对象就是多个线程间有效通信手段。- Object.wait() 必须包含在 synchronzied 语句中,无论是 wait() 还是 notify() 都需要先获得目标对象的一个监视器。(PS:这两个方法都是执行后立刻释放监视器,防止其他等待对象线程因为该线程休眠而全部无法正常执行)
- Object.wait() 同样会抛出
InterruptedException
异常。 - Object.notifyAll() 方法会唤醒等待队列中所有等待的线程。
suspengd()与resume()
前者是线程挂起,后者是继续执行,这是一对互相配合的方法。这两个也是已经被废弃的方法,了解一下就行了。- suspend() 会导致休眠线程的所有资源都不会被释放,直到执行 resume() 方法。(如果两个方法,后者执行于前者之前,则线程很难有机会被继续执行)
- 对于被挂起的线程,其状态是
Runnable
,这会严重影响到我们对于系统当前状态的判断。(不可饶恕)
join()与yield()
join() 方法
其签名如下:
public final void join() throws InterruptedException;
public final void join(long millis) throws InterruptedException;
第一个方法表示无限等待;第二个方法表示一定时间的等待。join()方法会阻塞当前线程,直到目标线程结束,或者到达阻塞时间。
同时,join()方法的本质是让在当前线程对象实例上调用线程的wait()方法。yield() 方法
其签名如下:
public static native void yield();
这个静态方法会使当前线程让出CPU。(PS:但还是会进行CPU资源的抢夺)
当某个线程不是那么重要,或者优先级别较低,可以在适当时候调用 Thread.yield(),给予其他重要线程更多工作机会。
关于线程操作的补充
- sleep() 与 wait() 的区别
Object.wait() 和 Thread.sleep() 都可以让线程等待若干时间。除了** wait() 可以被唤醒外,另外一个主要区别:**wait() 方法会释放目标对象的锁,而 sleep() 方法不会释放任何资源。
三、分门别类的管理:线程组
简单建立一个线程组(代码来源于《实战Java高并发程序设计》书中)
public class MyThread implements Runnable { public static void main(String[] args) {
//创建一个叫"PrintGroup"的线程组
ThreadGroup tg = new ThreadGroup("PrintGroup");
Thread t1 = new Thread(tg, new MyThread(), "T1"); //加入线程组
Thread t2 = new Thread(tg, new MyThread(), "T2");
t1.start();
t2.start();
//由于线程是动态的,activeCount()获得活动线程总数的估算值
System.out.println("活动线程总数 = " + tg.activeCount());
//打印出线程组的线程信息
tg.list();
//注意这个方法与Thread.stop()方法遇到问题一样
tg.stop();
} @Override
public void run() {
String groupAndName = Thread.currentThread().getThreadGroup().getName()
+ "---" + Thread.currentThread().getName();
while (true) {
System.out.println("I am " + groupAndName);
try {
Thread.sleep(3000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
四、驻守后台的守护线程
守护线程,是系统的守护者,在后台完成系统性的服务,比如垃圾回收线程、JIT线程等。当一个Java应用内只有守护线程时,Java虚拟机就会自然退出。
将线程设置为守护线程:
Thread t = new Thread(new MyThread());
t.setDaemon(true);
t.start();
注意:设置守护线程必须在线程start()之前设置,否则会得到一个IllegalThreadStateException
,但程序和线程依然可以正常执行,只是线程被当作用户线程而已。
五、线程优先级
Java中线程可以有自己的优先级。由于线程的优先级调度和底层操作系统有密切关系,在各个平台表现不一,无法精准控制。
在Java中,使用 1 到 10 表示线程优先级。一般可以使用三个内置的静态标量表示:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
PS:数字越大,优先级越高(一般情况)。
六、synchronized 关键字
synchronized 关键字用法简单记录:
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
- 直接作用于实例对象:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。
synchronized 可以保证线程间的原子性、可见性和有序性(被 synchronized 限制的多个线程是串行执行的)。
七、部分隐秘的错误记录
- 执行(synchronized)同步代码的两个线程指向了不同的Runnable实例,即两个线程使用了两个不同的锁。解决方法:将同步代码修改为静态(static)方法。
- 使用线程不安全的容器,如 ArrayList、HashMap 等。解决方法:改用线程安全的容器,如 Vector、ConcurrentHashMap。
- 不变对象加锁,导致对于临界区代码控制出现问题。
- 不变对象:对象一旦被创建,就不能修改。
(如,Integer i; i++
的本质是创建一个新的Integer对象,并将引用赋值给i,同时这里涉及到Integer引用的特性)
- 不变对象:对象一旦被创建,就不能修改。
八、参考资料
- 《实战Java高并发程序设计》(葛一鸣 郭超 著)
JAVA并行程序基础的更多相关文章
- Java并发程序设计(二)Java并行程序基础
Java并行程序基础 一.线程的生命周期 其中blocked和waiting的区别: 作者:赵老师链接:https://www.zhihu.com/question/27654579/answer/1 ...
- JAVA并行程序基础二
JAVA并行程序基础二 线程组 当一个系统中,如果线程较多并且功能分配比较明确,可以将相同功能的线程放入同一个线程组里. activeCount()可获得活动线程的总数,由于线程是动态的只能获取一个估 ...
- JAVA并行程序基础一
JAVA并行程序基础一 线程的状态 初始线程:线程的基本操作 1. 新建线程 新建线程只需要使用new关键字创建一个线程对象,并且用start() ,线程start()之后会执行run()方法 不要直 ...
- 第2章 Java并行程序基础(三)
2.8 程序中的幽灵:隐蔽的错误 2.8.1 无提示的错误案例 以求两个整数的平均值为例.请看下面代码: int v1 = 1073741827; int v2 = 1431655768; Syste ...
- Java并行程序基础。
并发,就是用多个执行器(线程)来完成一个任务(大任务)来处理业务(提高效率)的方法.而在这个过程中,会涉及到一些问题,所以学的就是解决这些问题的方法. 线程的基本操作: 1.创建线程:只需要new一个 ...
- 第2章 Java并行程序基础(二)
2.3 volatile 与 Java 内存模型(JMM) volatile对于保证操作的原子性是由非常大的帮助的(可见性).但是需要注意的是,volatile并不能代替锁,它也无法保证一些复合操作的 ...
- 第2章 Java并行程序基础(一)
2.1 有关线程你必须知道的事 进程是系统进行资源分配和调度的基本单位,是程序的基本执行实体. 线程就是轻量级进程,是程序执行的最小单位. 线程的生命周期,如图2.3所示. 线程的所有状态都在Thre ...
- 到头来还是逃不开Java - Java13程序基础
java程序基础 没有特殊说明,我的所有学习笔记都是从廖老师那里摘抄过来的,侵删 引言 兜兜转转到了大四,学过了C,C++,C#,Java,Python,学一门丢一门,到了最后还是要把Java捡起来. ...
- Spring MVC + Spring + Mybitis开发Java Web程序基础
Spring MVC + Spring + Mybitis是除了SSH外的另外一种常见的web框架组合. Java web开发和普通的Java应用程序开发是不太一样的,下面是一个Java web开发在 ...
随机推荐
- 20145307陈俊达《网络对抗》Exp9 Web安全基础实践
20145307陈俊达<网络对抗>Exp9 Web安全基础实践 基础问题回答 1.SQL注入攻击原理,如何防御? SQL注入攻击就是通过把SQL命令插入到Web表单递交或输入域名或页面请求 ...
- 20144303石宇森 《网络对抗》 WEB基础实践
20144303石宇森 <网络对抗> WEB基础实践 实验后回答问题 一.什么是表单 表单是一个包含表单元素的区域.用form来定义. HTML是静态显示网页的,无法跟服务器进行交互,所以 ...
- React 回忆录(二)为什么使用 React?
Hi 各位,欢迎来到 React 回忆录!
- git下载速度太慢【学习笔记】
使用了sshFQ的伙伴添加这个配置下载速度有极大的提升. git config --global http.proxy 'socks5://127.0.0.1:1080'
- HDU 2841 Visible Trees(容斥)题解
题意:有一块(1,1)到(m,n)的地,从(0,0)看能看到几块(如果两块地到看的地方三点一线,后面的地都看不到). 思路:一开始是想不到容斥...后来发现被遮住的地都有一个特点,若(a,b)有gcd ...
- UVA 10462 Is There A Second Way Left?(次小生成树&Prim&Kruskal)题解
思路: Prim: 这道题目中有重边 Prim可以先加一个sec数组来保存重边的次小边,这样不会影响到最小生成树,在算次小生成树时要同时判断次小边(不需判断是否在MST中) Kruskal: Krus ...
- POJ 3162 Walking Race(树形dp+单调队列 or 线段树)
http://poj.org/problem?id=3162 题意:一棵n个节点的树.有一个屌丝爱跑步,跑n天,第i天从第i个节点开始跑步,每次跑到距第i个节点最远的那个节点(产生了n个距离),现在要 ...
- UVa 11491 奖品的价值
https://vjudge.net/problem/UVA-11491 题意:一个n位整数,删除其中的d个数字,输出最大值. 思路:肯定从高位开始分析,代码如下. #include<strin ...
- python flask demo
from flask import Flask, jsonify from flask import abort from flask import make_response from flask ...
- Springboot mybatis generate 自动生成实体类和Mapper
https://github.com/JasmineQian/SpringDemo_2019/tree/master/mybatis Springboot让java开发变得方便,Springboot中 ...