首页 / JAVA / Java 多线程 01
Java 多线程 01
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java 多线程 01,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5239字,纯文字阅读大概需要8分钟。
内容图文
进程和线程
进程:process,线程:thread
进程是资源分配的基本单位;线程是程序执行的基本单位。
进程拥有自己的资源空间,每启动一个进程,系统就会为它分配地址空间;而线程与CPU资源分配无关,多个线程共享同一进程内的资源,使用相同的地址空间。
同一个进程的多个线程虽然共享程序的内存空间(堆空间等等),但各个线程有独立的栈空间。
多核计算机(6核12线程)看到的是线程,指的是同时有12个线程可以并行
线程的启动方式:
继承Thread类方式,实现Runnable接口方式,Callable接口方式。
继承Thread方式:
public class ThreadMine extends Thread{ @Override publicvoid run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"计数第"+i+"次"); } } publicstaticvoid main(String[] args) { ThreadMine threadMine = new ThreadMine(); threadMine.setName("secondThread"); threadMine.start(); for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"计数第"+i+"次"); } } }
直接继承Thread类,重写run方法,new新类的实例,调用.start()即可。
实现Runnable接口的方式:
public class ThreadRun implements Runnable{ publicvoid run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"计数第"+i+"次"); } } publicstaticvoid main(String[] args) { ThreadRun threadRun = new ThreadRun(); new Thread(threadRun,"Thread1").start(); new Thread(threadRun,"Thread2").start(); new Thread(threadRun,"Thread3").start(); } }
新类实现Runnable接口,重写run方法。new Thread类的实例,参数中放入新类,直接.start()即可。
之所以我们重写的run方法,但调用start方法,是因为静态代理模式,Thread类代理了Runnable接口。
显然采用Runnable接口重写方式有几个好处:
- 这个逻辑关系更像实现接口而非继承
- 这样可以用同一个类(同一种run方法)开好多个新的线程
Lamada表达式
Lamada表达式来直接简写创建线程:
线程状态
System.out.println(threadMine.getState().toString());//可以输出某个线程的状态
线程状态控制
如何结束线程
- 用stop()和suspend()方法终止,但这两种方法已经过时了,不建议使用。原理上,stop我的理解是会解锁对象,导致有的对象发生不一致。suspend则会有导致死锁的倾向。(这里的原理还需要进一步了解)
- 置标志位停止,其实是让run方法自行结束。注意,把这个标志位设成volatile的,这表示一个时间内只有一个线程能访问它(但是这个关键字的原理并不是加锁,而是告诉了cpu这个共享的值随时可能被其他线程修改-可见性):https://zhuanlan.zhihu.com/p/42497046
public class ThreadStatus extends Thread{ publicvolatileboolean flag=true; publicvoid run() { while (flag) { System.out.println(Thread.currentThread().getName() + "线程正在跑着"); } System.out.println("线程结束了"); } publicstaticvoid main(String[] args) { ThreadStatus threadStatus = new ThreadStatus(); threadStatus.setName("second"); threadStatus.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadStatus.flag=false; } }
这种方式只有当run方法在运行的时候才有用,如果线程处于非运行状态(等待,计时等待,阻塞),这种方式就没用了。如果线程被阻塞,它便不能核查共享变量,也就不能停止。此时可以用interrupted()方法打破这种状态。其中断状态将被清除,它还将收到一个InterruptedException异常。这个时候,我们可以通过捕获InterruptedException异常来终止线程的执行,具体可以通过return等退出或改变共享变量的值使其退出。
具体来说,当对一个线程,调用 interrupt() 时,
interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
① 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。
② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。
也就是说,一个线程如果有被中断的需求,那么就可以这样做。
① 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
② 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
public class ThreadInterrupt implements Runnable{ publicvoid run() { try { while (!Thread.currentThread().isInterrupted()) { Thread.sleep(1000);//模仿线程定时等待 System.out.println("该线程在跑着"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("该线程结束"); } } publicstaticvoid main(String[] args) throws InterruptedException { Thread thread = new Thread(new ThreadInterrupt()); thread.start(); Thread.sleep(3000); thread.interrupt(); } }
当线程被I/O阻塞的时候,调用interrupt()的情况是依赖与实际运行的平台的。在Solaris和Linux平台上将会抛出InterruptedIOException的异常,但是Windows上面不会有这种异常。
所以处理方式也是在run方法中捕获异常,只是捕获的异常不一样
Thread.sleep()定时等待与wait()方法
好像面试常问
整体的区别其实是有四个:
1、sleep是线程中的方法,但是wait是Object中的方法。
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。
第一点:好理解,sleep是Thread的静态方法,wait是Object类中的方法。sleep()是线程用来控制自身流程的,把自身暂停一段时间,把执行机会让给其他线程。wait方法用于线程间的通信,这个方法会使拥有该对象锁的进程等待,直到其他线程调用notify()方法,也可以加参数定时。
第二点:sleep是不会释放锁的,看如下例子
public class ThreadSleep implements Runnable{ publicstatic Object lock=new Object(); publicvoid run() { synchronized (lock) { System.out.println(Thread.currentThread().getName() + "在运行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "结束运行"); } } publicstaticvoid main(String[] args) { ThreadSleep threadSleep = new ThreadSleep(); new Thread(threadSleep,"线程1").start(); new Thread(threadSleep,"线程2").start(); } }
输出结果:
线程1在sleep时并没有释放锁lock,只有它完成后线程2才能拿到锁开始执行。
public class Test { private final static Object lock = new Object(); publicstaticvoid main(String[] args) { Stream.of("线程1", "线程2").forEach(n -> new Thread(n) { publicvoid run() { Test.testWait(); } }.start()); } privatestaticvoid testWait() { synchronized (lock) { try { System.out.println(Thread.currentThread().getName() + "正在执行"); lock.wait(10_000); System.out.println(Thread.currentThread().getName() + "wait结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
第三点:依赖同步方法好理解,wait锁的是同步方法中的对象,没有同步方法怎么行。
由于sleep不会释放锁,容易导致死锁问题产生,更加推荐wait方法
线程礼让 yield
当前线程由运行状态转为就绪状态。
和sleep不一样,只会给相同/更高优先级的线程运行的机会
也不会释放锁
线程插队 join
据说了解一下就行
输出线程状态的方式
守护线程
原文:https://www.cnblogs.com/take-it-easy/p/14009577.html
内容总结
以上是互联网集市为您收集整理的Java 多线程 01全部内容,希望文章能够帮你解决Java 多线程 01所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。