内容简介:并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效率。下面提供一个基础的演示案例。场景:假设有一个容器集合,需要拿出容器中的每个元素,进行加工处理,一般情况下直接遍历就好,如果数据偏大,可以根据线程数量对集合切割,每个线程处理一部分数据,这样处理时间就会减少很多。注意:这里案例只是对场景原理的实现,在开发中,是不允许这种操作的,需要使用线程池处理,后续会说。如果集合没有控制好,导致大量创建Thread线程,导致内存溢出。
一、线程基本机制
1、概念描述
并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效率。下面提供一个基础的演示案例。
2、应用案例
场景:假设有一个容器集合,需要拿出容器中的每个元素,进行加工处理,一般情况下直接遍历就好,如果数据偏大,可以根据线程数量对集合切割,每个线程处理一部分数据,这样处理时间就会减少很多。
public class ExtendThread01 {
public static void main(String[] args) {
List<Object> dataList = new ArrayList<>() ;
dataList.add("A");
dataList.add("B");
dataList.add("C");
// 把一个大的集合按照每个子集合的2个元素切割
List<List<Object>> splitList = splitList(dataList,2);
for (List<Object> list:splitList){
System.out.println(list);
}
// 多线程处理
for (List<Object> childList:splitList){
ListTask listTask = new ListTask(childList) ;
Thread runThread = new Thread(listTask);
runThread.start();
}
}
/**
* List 集合切割
*/
private static List<List<Object>> splitList (List<Object> list, int childSize) {
if (list == null || list.size() == 0 || childSize < 1) {
return null;
}
List<List<Object>> result = new ArrayList<>();
int size = list.size();
int count = (size + childSize - 1) / childSize ;
for (int i = 0; i < count; i++) {
List<Object> subList = list.subList(i * childSize, ((i + 1) * childSize > size ? size : childSize * (i + 1)));
result.add(subList);
}
return result;
}
}
class ListTask implements Runnable {
private List<Object> list ;
public ListTask (List<Object> list){this.list=list;}
@Override
public void run() {
for (Object object:list){
System.out.println(Thread.currentThread().getName()+"=="+object);
}
}
}
注意:这里案例只是对场景原理的实现,在开发中,是不允许这种操作的,需要使用线程池处理,后续会说。如果集合没有控制好,导致大量创建Thread线程,导致内存溢出。
二、线程停止启动
1、基础流程
线程启动后执行任务方法,在执行过程中可以被阻塞,休眠,唤醒,停止等一系列状态操作。
线程休眠作用:当线程一部分任务执行完毕后进入休眠(阻塞)状态,线程调度器可以切换到另外线程,这对分布任务的执行相对公平。
2、使用案例
public class ExtendThread02 {
public static void main(String[] args) {
StopThread stopThread = new StopThread() ;
stopThread.start();
// 标记当前线程停止信号,且抛出中断异常,但没有停止
stopThread.interrupt();
// 判断当前线程是否已经是终止状态
System.out.println("1=="+stopThread.isInterrupted());
// 清除当前线程的终止信号
System.out.println("2=="+stopThread.interrupted());
// 再次判断当前线程状态
System.out.println("3=="+stopThread.isInterrupted());
System.out.println("main end ...");
}
}
class StopThread extends Thread {
@Override
public void run() {
for (int i = 0 ; i < 10 ; i++){
try {
System.out.println(Thread.currentThread().getId()+"="+i);
// 线程阻塞1秒
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
3、核心方法
sleep(long millis)
:线程休眠指定的时间,进入阻塞状态;
interrupt()
:切换线程为中断状态,抛出中断异常,不会停止线程,可以监视线程的中断状态并定义执行策略。
interrupted()
:清除调用该方法线程的中断状态,也不会影响线程的执行,且返回当前执行‘stopThread.interrupted()’的线程是否中断,这里就是指main线程是否中断。
isInterrupted()
:判断调用该方法的线程是否已经是中断状态。
补刀一句
:线程的这几个方法极其容易混淆,需要断点源码追踪一下看看,进入源码方法,调用相关API查看一下状态。(附断点图一张:)
三、线程优先级
1、基础概念
CPU执行和处理线程的顺序是不确定的,但是线程调度器倾向执行线程优先级高的线程,线程优先级高说明获取CPU资源的概率高,或者获取的执行时间分片多, 但 不代表优先级低的一定最后执行。
2、使用案例
public class ExtendThread03 {
public static void main(String[] args) {
Priority01 priority01 = new Priority01();
priority01.start();
System.out.println("priority01="+priority01.getPriority());
Priority02 priority02 = new Priority02();
priority02.start();
System.out.println("priority02="+priority02.getPriority());
priority01.setPriority(10);
priority02.setPriority(1);
}
}
class Priority01 extends Thread {
@Override
public void run() {
for (int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+";i="+i);
}
}
}
class Priority02 extends Thread {
@Override
public void run() {
for (int a = 0 ; a < 100 ; a++){
System.out.println(Thread.currentThread().getName()+";a="+a);
}
}
}
注意
:优先级范围[MAX_PRIORITY=10,MIN_PRIORITY=1],如果超出范围会抛出IllegalArgumentException异常。
建议
:通常实际开发中,是不允许轻易修改线程运行的参数,容易引发认知之外的异常。
四、线程加入
1、基本概念
如果在线程A中,执行线程B的加入方法,那么A线程就会等待线程B执行完毕再返回继续执行。
2、使用案例
public class ExtendThread04 {
public static void main(String[] args) {
JoinThreadA joinThreadA = new JoinThreadA() ;
joinThreadA.start();
}
}
class JoinThreadA extends Thread {
@Override
public void run() {
System.out.println("缺水中...");
JoinThreadB joinThreadB = new JoinThreadB() ;
joinThreadB.start();
try{
joinThreadB.join();
} catch (Exception e){
e.printStackTrace();
}
System.out.println("喝水中...");
}
}
class JoinThreadB extends Thread {
@Override
public void run() {
System.out.println("买水中...");
try{
TimeUnit.SECONDS.sleep(2);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("买到水...");
}
}
注意
:可以设置线程的加入时间join(long),毕竟等不到雪月风花,人生都是有时差,只能后会无期了。
五、本地线程
1、基本概念
本地的线程变量,底层维护ThreadLocalMap存储值:
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
即以Key-Value键值对的方式存储数据。如果对集合容器的源码熟悉的话,这个Entry就是似曾相识感觉。
2、使用案例
public class ExtendThread05 {
private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>() ;
private static void initBegin (){
threadLocal.set(System.currentTimeMillis());
}
private static Long overTime (){
return System.currentTimeMillis()-threadLocal.get();
}
public static void main(String[] args) throws Exception {
ExtendThread05.initBegin();
TimeUnit.SECONDS.sleep(3);
System.out.println(ExtendThread05.overTime());
}
}
ThreadLocal提供线程内存储变量的能力,并且绑定到当前线程,通过get和set方法就可以得到和设置当前线程对应的值。这个在web开发中是常见的应用。
六、守护线程
1、基本概念
守护线程是支持辅助型线程,主要在程序中起到调度和支持性作用,当Jvm中非守护线程全部结束,守护线程也就会结束。
2、使用案例
public class ExtendThread06 {
public static void main(String[] args) throws Exception {
InputStreamReader is = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(is);
String value = br.readLine();
CheckThread checkThread = new CheckThread(value) ;
checkThread.setDaemon(true);
checkThread.start();
System.out.println("Main End ...");
}
}
class CheckThread extends Thread {
private String spell ;
public CheckThread (String spell){
this.spell = spell ;
}
@Override
public void run() {
if (spell.startsWith("cs")){
System.out.println(spell+":输入正确");
} else {
System.out.println(spell+":输入错误");
}
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
注意
:守护线程需要显式调用setDaemon(true);方法,这里可以看到main线程结束,整合程序就结束了,丝毫理会休眠中的守护线程。如果打断非守护线程的休眠,试试会不会抛你一脸异常?
七、线程异常处理
1、机制描述
按照 Java 中异常处理机制,抛异常逐级降低,线程的任务方法run没有抛异常,那重写或者实现的方法自然不能直接throws异常出去。多线程中推荐捕获异常,可以针对性处理机制。
2、使用案例
public class ExtendThread07 {
public static void main(String[] args) {
TryThread tryThread = new TryThread();
tryThread.setName("try-name");
// 定义运行中异常处理策略
MyExe myExe = new MyExe() ;
tryThread.setUncaughtExceptionHandler(myExe);
tryThread.start();
}
}
class TryThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
// 如何处理这里异常?
Integer.parseInt("cicada") ;
}
}
class MyExe implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName()+";异常:"+e.getMessage());
}
}
通过实现UncaughtExceptionHandler接口,并且线程要指定自定义异常处理对象,也可以处理未检查的异常。
八、源代码地址
GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Designing for Emotion
Aarron Walter / Happy Cog / 2011-10-18 / USD 18.00
Make your users fall in love with your site via the precepts packed into this brief, charming book by MailChimp user experience design lead Aarron Walter. From classic psychology to case studies, high......一起来看看 《Designing for Emotion》 这本书的介绍吧!