摘要:另外一种方法是,将这个线程加入一个线程组,在线程组里重写方法来处理抛出的异常,这时线程组的作用相当于实现了的类。使用对象处理异常格式错误使用线程组处理异常测试异常
感性地理解一下什么是线程?
线程这个概念其实是比较抽象的,虽然依照教科书上的说法:
进程是从系统获取资源的最小单位,线程是程序执行的最小单位。程序是静态存在于磁盘上的一段文本,进程运行这段文本记录的命令。
也就是说,进程从系统那里获取到了一定的CPU占用时间片、内存单元和IO等等资源,然后线程将这些资源利用起来执行程序,线程执行程序是什么意思呢?就是把程序记录的那些命令逐条依序一步步在CPU上运作,数据在内存、IO上流转,将命令执行完。
这个层级的概念存在于OS上,OS的调度抽象层级并不是那么直观,如果我们在说明白一点,在做底层的计算机组成原理实验的时候,在我们接好连线后硬件就具有了处理数据的能力,只要扳动不同的开关就可以将数据读写在不同的芯片上,我们的程序也许是为了完成数据流转写在纸上的扳动不同开关的序列,所以程序是属于IO级别的,然后我们依照纸上的命令序列实际上手去扳动不同的开关执行的就是这段程序,所以我们自己充当的角色就是进程,最终就得出了这样的结论:进程“执行”程序。至于线程呢,可以看作是进程在执行过程中的策略,比如说在一个人扳动开关的时候就是单进程单线程,如果是两个人扳动开关就是单进程多线程,如果两个人能配合起来扳动开关就是多线程同步,所以线程和进程之间并非互斥的概念,而是相容的概念,如果有线程就一定有进程,一个进程包含了至少一个的线程。
创建线程的方法
1.创建直接创建Thread的子类,重写run()方法;
class MyThread extends Thread { @Override public void run() { System.out.println("This is my thread"); } } public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }
2.创建一个线程执行类实现Runnable接口,在这个执行类里实现Runnable的run()方法,创建该执行类的对象后,用此执行类对象初始化新线程,启动新线程时即执行这个执行对象的run()方法;
class MyRunnable implements Runnable { @Override public void run() { System.out.println("This is my thread"); } } public class Test { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); } }
3.通过线程工厂用工厂模式来创建新线程,新建工厂类继承ThreadFactory类重写newThread()方法,通过指定实现了Runnable接口的执行类来创建与之对应的线程;
public class ThreadFactoryDemo { public static void main(String[] args) { ThreadFactory factory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { // TODO Auto-generated method stub return new Thread(r); } }; factory.newThread(new Runnable() { @Override public void run() { System.out.println("in runnable."); } }).start(); } }
注意:只有调用Thread类的Start方法,才能真正地在一个独立的线程中执行代码,直接调用Thread类的run方法,并不能启动一个新的线程,代码是在调用者线程中执行的。
那么主线程的run()方法在哪里呢?任何java程序的main执行入口担当着启动主线程的作用,只要进入了main函数就执行了主线程,因此整个main函数里的内容就是主线程的run()方法。
线程究竟执行哪个run()方法
当线程同时具有可执行对象实现的run()方法和线程重写的run()方法时,启动线程时究竟执行哪个run()方法呢?
结果是如果只定义了可执行对象的run()方法则执行这个run()方法,如果只重写了线程的run()方法则执行这个run()方法,如果两个方法都有则执行线程重写的run()方法。
public class Test { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Runnable.run()"); } }) { @Override public void run() { // TODO Auto-generated method stub System.out.println("Thread.run()"); } }; thread.start(); } }
线程的休眠
使用Thread类的sleep()方法或者使用TimeUnit的相关方法来休眠线程,休眠的意思是资源仍被占用,但是线程保留原来的状态没有活动;
public class ThreadSleep { public static void main(String[] args) { Thread th = new Thread(new Runnable() { public void run() { for (int i = 0; i < 10; i++) { try { // Thread.sleep(500); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }); th.start(); } }
线程中断
线程中断的意思是线程停止当前的运行状态让出资源结束生命周期,当外界想要一个线程中断时需要调用它的interrupted()方法,调用后不是直接就可以中断这个线程,而是将线程的interrupted标记位赋为1,如果要线程要响应这个中断则定期需要检查这个标记,检查到被中断标记后自己退出执行状态。
public class ThreadInterruptDemo { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { while (true) { System.out.println("running"); if (isInterrupted()) return; } } }; thread.start(); Thread.sleep(2000); thread.interrupt(); } }
线程定时任务
线程要实现定时任务的话可以使用Runnable的实现类TimerTask,此类需要重写run()方法以完成具体需要进行的定时任务。然后由定时器Timer来调度,使用Timer的schedle()方法相当于启动这个定时任务线程。
public class TimerTaskDemo { public static void main(String[] args) { TimerTask task = new TimerTask() { private int counter = 0; @Override public void run() { System.out.println(counter + ":invoked!"); counter++; } }; Timer timer = new Timer(); // 过2秒钟后首次运行,以后每隔3秒运行一次 timer.schedule(task, 2000, 3000); } }
线程运行过程中的异常处理
线程的run()方法中是不允许直接抛出异常的,也就是说不能有这样的写法:run() throws Exception ,原因在于在线程的运行过程中应该最大限度地保持正常工作,因此除了一些不可预知的运行时异常,不应该主动抛出受控异常。如果非要在run()方法里处理抛出的异常,则应该定义一个实现了UncaughtExceptionHandler的类,然后指定这个类的对象在重写的uncaughtException()方法里去处理抛出的异常。另外一种方法是,将这个线程加入一个线程组,在线程组里重写uncaughtException()方法来处理抛出的异常,这时线程组的作用相当于实现了UncaughtExceptionHandler的类。
1.使用handler对象处理异常:
public class ThreadTest { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { throw new RuntimeException("格式错误"); } }); thread.setUncaughtExceptionHandler(new MyHandler()); thread.start(); } } class MyHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(t.getName() + ":" + e.getMessage()); } }
2.使用线程组处理异常:
public class ThreadGroupDemo { public static void main(String[] args) { ThreadGroup threadGroup1 = new ThreadGroup("group1") { public void uncaughtException(Thread t, Throwable e) { System.out.println(t.getName() + ": " + e.getMessage()); } }; Thread thread1 = new Thread(threadGroup1, new Runnable() { public void run() { throw new RuntimeException("测试异常"); } }); thread1.start(); } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/64617.html
摘要:线程可以被称为轻量级进程。一个守护线程是在后台执行并且不会阻止终止的线程。其他的线程状态还有,和。上下文切换是多任务操作系统和多线程环境的基本特征。在的线程中并没有可供任何对象使用的锁和同步器。 原文:Java Multi-Threading and Concurrency Interview Questions with Answers 翻译:并发编程网 - 郑旭东 校对:方腾飞 多...
摘要:多线程和并发问题是技术面试中面试官比较喜欢问的问题之一。线程可以被称为轻量级进程。一个守护线程是在后台执行并且不会阻止终止的线程。其他的线程状态还有,和。上下文切换是多任务操作系统和多线程环境的基本特征。 多线程和并发问题是 Java 技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(...
摘要:超详细的面试题总结一之基本知识多线程和虚拟机创建线程有几种不同的方式你喜欢哪一种为什么继承类实现接口应用程序可以使用框架来创建线程池实现接口。死亡线程方法执行结束,或者因异常退出了方法,则该线程结束生命周期。死亡的线程不可再次复生。 超详细的Java面试题总结(一)之Java基本知识 多线程和Java虚拟机 创建线程有几种不同的方式?你喜欢哪一种?为什么? 继承Thread类 实现R...
摘要:一线程的基本概念单线程简单的说,单线程就是进程中只有一个线程。多线程由一个以上线程组成的程序称为多线程程序。当线程调用完方法进入后会自动释放锁,线程获得锁。 一、线程的基本概念 1.1 单线程 简单的说,单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。 Java示例: public class SingleThrea...
摘要:基础知识复习后端掘金的作用表示静态修饰符,使用修饰的变量,在中分配内存后一直存在,直到程序退出才释放空间。将对象编码为字节流称之为序列化,反之将字节流重建成对象称之为反序列化。 Java 学习过程|完整思维导图 - 后端 - 掘金JVM 1. 内存模型( 内存分为几部分? 堆溢出、栈溢出原因及实例?线上如何排查?) 2. 类加载机制 3. 垃圾回收 Java基础 什么是接口?什么是抽象...
阅读 931·2021-09-26 09:55
阅读 3174·2021-09-22 15:36
阅读 2949·2021-09-04 16:48
阅读 3118·2021-09-01 11:41
阅读 2572·2019-08-30 13:49
阅读 1472·2019-08-29 18:46
阅读 3528·2019-08-29 17:28
阅读 3409·2019-08-29 14:11