复习资料:《同步与异步:并发/并行/进程/线程/多cpu/多核/超线程/管程 》
基本线程类指的是Thread类,Runnable接口,Callable接口
继承java.lang.Thread类创建线程是最简单的一种方法,也最直接。
public class MyThread1 extends Thread {}
种创建方式,把线程执行的逻辑代码直接写在了Thread的子类中,这样根据线程的概念模型,虚拟CPU和代码混合在一起了。并且java是单继承机制,线程体继承Thread类后,就不能继承其他类了,线程的扩展受影响。
为了构建结构清晰线程程序,可以把代码独立出来形成线程目标对象,然后传给Thread对象。通常,实现Runnable接口的类创建的对象,称作线程的目标对象。
public class MyThreadTarget implements Runnable{ }
出线程目标对象和Thread分开了,并传递给了Thread。如果有比较复杂的数据要处理,可以在线程目标对象中引入数据。使用这种方式获得线程的名字就稍微复杂一些,需要使用到Thread中的静态方法,获得当前线程对象,然后再调用getName()方法。这种方式在较复杂的程序中用得比较普遍。
future模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是isDone和get。其中Future对象用来存放该线程的返回值以及状态
ExecutorService e = Executors.newFixedThreadPool(3);
//submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
Future future = e.submit(new myCallable());
future.isDone() //return true,false 无阻塞
future.get() // return 返回值,阻塞直到该线程运行结束
以上是基本方法,总结一下
推荐方式二,因为接口方式比继承方式更灵活,也减少程序间的耦合。
推荐 使用 interrupt() +抛异常 中断线程。
Thread.currentThread()
线程分为守护线程、用户线程。线程初始化默认为用户线程。
setDaemon(true) 将该线程标记为守护线程或用户线程。
特性:设置守护线程,会作为进程的守护者,如果进程内没有其他非守护线程,那么守护线程也会被销毁,即使可能线程内没有运行结束。
某线程a 中启动另外一个线程t,那么我们称线程t是线程a的一个子线程,而 线程a是线程t的父线程。
最典型的就是我们在main方法中 启动一个线程去执行。其中main方法隐含的main线程为父线程。
线程安全就是每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的。
线程安全就是说多线程访问同一代码,不会产生不确定的结果。
反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
线程安全问题多是由全局变量和静态变量引起的
同步是保证多个线程之间共享同一个全局变量数据安全问题,保证数据原子性。
多个线程操作一个资源的情况下,导致资源数据前后不一致。这样就需要协调线程的调度,即线程同步。 解决多个线程使用共通资源的方法是:线程操作资源时独占资源,其他线程不能访问资源。使用锁可以保证在某一代码段上只有一条线程访问共用资源。
有两种方式实现线程同步:
Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
线程状态
当它用来修饰一个方法或者一个代码块的时候(同步代码块:将可能会发生线程安全问题的代码给包裹起来),能够保证在同一时刻最多只有一个线程执行该段代码。
如果在synchronized锁内发生异常,锁会被释放。
效率非常低,多个线程需要判断锁,比较消耗资源,抢锁的资源。——synchronized 只能有一个线程进行执行(就像厕所只有一个坑,好多人等着上厕所,谁进入厕所谁上锁,其他人在外等待),这个线程不释放锁的话,其他线程就一直等,就会产生死锁问题。
多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
Volatile 关键字的作用是变量在多个线程之间可见,但不保证原子性。
针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。
线程间通信的方式主要为共享内存、线程同步。
线程同步除了synchronized互斥同步外,也可以使用wait/notify实现等待、通知的机制。
需要:wait被执行后,会自动释放锁,而notify被执行后,锁没有立刻释放,由synchronized同步块结束时释放。
应用场景:简单的生产、消费问题。
synchronized (lock) {//获取到对象锁lock
try {
lock.wait();//等待通信信号, 释放对象锁lock
} catch (InterruptedException e) {
e.printStackTrace();
}
}//接到通信信号
synchronized (lock) {
//获取到对象锁lock
lock.notify();//通知并唤醒某个正等待的线程
//其他操作
}//释放对象锁lock
synchronized, wait, notify 是任何对象都具有的同步工具。
他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。 wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。 当某代码并不持有监视器的使用权时(如上图的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。
参考文章:
Java多线程并发编程一览笔录 https://www.cnblogs.com/yw0219/p/10597041.html
Java 中的多线程你只要看这一篇就够了 https://juejin.im/entry/57339fe82e958a0066bf284f
阿里大牛详细讲解java多线程并发,PDT文档实战篇https://zhuanlan.zhihu.com/p/92958977
JAVA多线程之间实现同步+多线程并发同步解决方案 https://blog.csdn.net/yz2015/article/details/79436123
转载本站文章《java并发编程(1):Java多线程-基本线程类-基础知识复习笔记》, 请注明出处:https://www.zhoulujun.cn/html/java/KeyConcepts/8471.html
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。