多线程知识点总结(二)

第三章  线程间通信

32、除了等待/通知机制,采用sleep 和 while(true)也可以实现多个线程间的通信,但是有一个弊端是线程要不断的通过while语句轮询机制来检测某一个条件,这样就会浪费CPU资源,如果轮询的时间间隔太小,更加浪费CPU资源,如果太大,就会丢失某些数据。所以更好的解决方法是采用wait/notify机制。

33、在调用wait( )方法前线程必须获得该对象的对象级别锁,即只能在同步方法或者同步代码块中调用wait( )方法。如果调用wait()方法前没有会的适当的锁,就会抛出 IllegalMonitorStateException,它是RunTimeException的一个子类,因此不需要try-catch语句捕获异常。

34、在执行notify( )方法之后,当前线程不会马上释放该对象锁,wait()状态的线程也不会马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块,才会释放对象锁。

当wait()状态的线程运行完毕之后,它释放掉该对象锁,此时如果该对象没有再使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有获得该对象的通知,仍然会阻塞在wait状态,直到这个对象发出一个notify 或者  notifyAll.

35、线程状态切换示意图

36、每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调度,反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。

37、方法wait( )被执行后,会自动释放锁;执行完 notify( )方法后,锁不会被自动释放,必须等到执行完同步代码块;sleep方法不会自动释放锁。

38、当线程呈wait( )状态时,调用对象的interrupt()方法会出现 InterruptException异常,这时线程会终止,锁也会释放。

39、如果通知过早,或者等待wait的条件发生变化,都会造成程序逻辑的混乱。

40、生产者消费者问题:

①一生产与一消费:操作值     这时可以正常运行

②多生产与多消费:操作值--假死。  “假死”的现象其实就是线程进入waiting状态。虽然在代码中已经通过wait/notify进行通信了,但是不能保证notify唤醒的是异类,也许是同类,比如“生产者”唤醒“生产者”,“消费者”唤醒“消费者”,累积下来,就会造成所有的线程都处于waiting状态,导致“假死”。解决假死的办法是 将 notify( )改为 notifyAll( ),这样可以不仅唤醒同类,也可以唤醒异类。

③一生产与一消费:操作栈    就是生产者向堆栈List对象中放入数据,消费者从List堆栈中取出数据。可以运行正常。

④一生产与多消费:操作栈    wait条件改变时可能没有得到及时的响应,所以多个呈wait状态的线程被唤醒,因此后面会出现异常,解决方法是 将逻辑判断条件的  if  改为 while;解决假死的方法是 notifyAll( )方法。

⑤多生产与一消费:操作栈    同样逻辑条件是while语句,为了避免假死 ,使用notifyAll

⑥多生产与多消费:操作栈    同样逻辑条件是while语句,为了避免假死 ,使用notifyAll

41、使用管道流(pipeStream)是一种特殊的流,用于在不同的线程间直接传递数据,一个线程发送数据到输出管道,另外一个先后才能从输入管道中读取数据。

在Java中提供了4个类来使线程间进行通信:

①PipedInputStream 和 PipedOutputStream 传递的是字节流

②PipedReader 和 PipedWriter 传递的是字符流

42、方法join的使用

① 方法join的作用是使所属的线程对象x 正常执行run( )方法中的任务,而使当前线程z进行无限期的等待(比如说一个主线程和一个子线程,让子线程执行run()方法,主线程一直等待),等待线程x销毁后再继续执行线程z 后面的代码。

②方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait方法进行等待,而synchronized关键字使用的是“对象监视器”原理做同步。

③在join的过程中,如果当前线程对象被中断,则当前线程出现异常。

④join(long) 的功能在内部是使用wait(long) 方法来实现的,所以 join(long) 释放锁;sleep(long) 方法不释放锁。

43、类ThreadLocal可以解决的问题是每个线程绑定自己的值。

使用的方法如下:

在第一次调用ThreadLocal类的get( )方法时,其返回值为null,设置默认值的方法是:

44、类InheritableThreadLocal 可以在子线程中取得父线程继承下来的值

使用InheritableThreadLocal类需要注意的一点是,如果子线程在取得值的同时,主线程将 InheritableThreadLocal 中的值进行更改,那么子线程取到的值还是旧值。

第四章  Lock的使用

45、等待/通知模式的两种实现方式的对比:

使用类 ReentrantLock 借助于 Condition对象可以实现等待/通知模式,具有更好的灵活性,可以实现多路通知的功能,也就是说在一个lock 对象里面可以创建多个 Condition (即对象监视器)实例,线程对象可以注册在指定的 Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。

使用synchronized 相当于整个Lock对象中只有一个单一的Condition 对象,所有的线程都注册在它一个对象的身上,线程开始 notifyAll() 时,需要通知所有的 WAITING  线程,没有选择权,会出现很大的效率问题。

46、Condition实现等待/通知:

Object类中的wait( )方法相当于Condition类中的  await( )方法;

Object类中的notify( )方法相当于Condition类中的  signal( )方法;

Object类中的notifyAll( )方法相当于Condition类中的  signalAll( )方法;

47、锁 Lock可以分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得先进先出的顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果就是不公平的了。

48、lock的常用方法总结:

① lock.getHoldCount( )的作用是查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。

② lock.getQueueLength( )的作用是返回正等待获取此锁定的线程估计数。

③ lock.getWaitQueueLength(Condition condition)的作用是返回等待与此锁定相关的给定条件Condition的线程估计数。

④ lock.hasQueuedThread(Thread  thread) 的作用是查询指定的线程是否正在等待获取此锁定。

⑤ lock.hasQueuedThreads( ) 的作用是查询是否有线程正在等待获取此锁定。

⑥ lock.hasWaiters(Condition condition) 的作用是查询是否有线程正在等待与此锁定有关的condition条件。

⑦ lock.isFair( )的作用是判断是不是公平锁。(默认ReentrantLock类使用的是非公平锁)

⑧ lock.isHeldByCurrentThread( )的作用是查询当前线程是否保持此锁定。

⑨ lock.isLocked( )的作用是查询此锁定是否由任意线程保持。

⑩ lock.lockInterruptibly( )的作用是:如果当前线程未被中断,则获取锁定,如果已经中断则出现异常。

(11)lock.tryLock( )的作用是:仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。

(12)lock.tryLock( long timeout,TimeUnit unit)的作用是,如果锁定在给定等待时间内没有被另一个线程保持,切当前线程未被中断,则获取该锁定。

49、使用Condition 对象可以对线程执行的业务进行排序规划。

50、类 ReentrantLock 具有完全互斥排他的效果,即同一时间只有一个线程在执行 ReentrantLock.lock( ) 方法后面的任务,可以使用读写锁ReentrantReadWriteLock 来提升运行速度。

第五章  定时器Timer

51、在实际业务开发中,完成定时任务常采用的是 Spring 定时任务之 @Scheduled cron表达式,而不是采用Timer完成定时任务。

52、定时计划任务功能在Java中主要使用的就是 Timer 对象,它在内部使用 多线程的方式进行处理。

53、Timer 类的主要作用就是设置计划任务,但封装任务的类是 TimerTask 类,因此需要将执行任务的代码放入到 TimerTask的子类中,因为TimerTask 是一个抽象类。

54、Timer 类的使用,创建一个Timer 就是启动一个新的线程

//并不是守护线程,任务虽然执行完了,但是进程并没有销毁,是红色状态

private static Timer timer = new Timer();

//设置成守护线程,程序运行后迅速结束当前线程,并且TimerTask中的任务不再被运行,因为进程已经结束了

private static Timer timer = new Timer(true);

在实际项目的开发中,就是要求不能是守护线程,因为守护线程的话,TimerTask类中的任务就不能执行了。

55、如果计划的时间早于当前时间,那么任务就会立即执行task 任务。

56、Timer类中允许有多个 TimerTask任务,TimerTask任务是以队列的方式一个一个被顺序执行的,所以执行的时间与预期的时间可能不一样,因为前面的任务可能消耗的时间比较长,则后面的任务执行的时间也要被延迟。举个例子:比如是每5分钟执行一次任务,如果前面的任务执行了6分钟,那么后面的任务不会在第五分钟的时候执行,它会延迟一分钟,在第六分钟的时候执行。

57、TimerTask类的cancel()方法:是将自身从任务队列中被移除,不会影响其他任务。比如一个类是TimerTask类的子类,即extends  TimerTask,那么调用cancel()方法的时候直接   this.cancel( ) 即可。

Timer类的cancel()方法:是将全部任务都被清除,并且进程被销毁,按钮由红色变成灰色,但是有时并不一定会停止执行计划任务,而是正常执行,因为有时并没有抢到 queue锁。

58、方法 schedule 和 方法 scheduleAtFixedRate的区别:

59、schedule方法不具有追赶执行性,scheduleAtFixedRate方法具有追赶执行性。

所谓追赶执行性,指的是在写代码的过程中,会写一个参考时间,以这个参考时间为基本,每隔多少秒执行一次,但是当程序运行到相应的语句时,时间肯定已经晚于设置的参考时间,那么在这个参考时间到当前的时间之间,如果按照设置的频率,那么应该会执行几次。采用 schedule方法,那么这段时间应该执行的那几次就不会执行了,直接从当前时间开始,按照设置的频率进行执行,而scheduleAtFixedRate方法就会在 当前时间的这一刻 将刚刚需要执行的几次补上,补够了再按照设置的频率执行。参考下面两个截图。

第六章  单例模式与多线程

60、在多线程的编程过程中,需要注意的是怎样保证单例模式时安全正确的。

61、立即加载(饿汉模式) 与 延迟加载(懒汉模式)

立即加载(饿汉模式):就是在使用类的时候已经将对象创建完毕,常见的实现办法是直接new 实例化,“饿汉模式”。

延迟加载(懒汉模式):就是在调用get( )方法时实例才被创建,常见的实现办法是在get( )方法中进行new实例化。但是在多线程的情况下,延迟加载就会取出多个实例。

62、解决在多线程的情况下延迟加载取出多个实例的情况:

① 声明synchronized 关键字同步方法,虽然可行,但是效率较低。

② synchronized同步代码块,同样可行,但是效率比较低。

③ synchronized同步代码块,但是只是针对部分重要的代码进行同步,不能保证是单个实例。

④ DCL(Double-Check  Locking)双检查锁机制(常用的解决方案)

⑤ 使用静态内置类实现单例模式

局限性:如果遇到序列化对象时,使用静态内置类得到的对象还是多例的。

⑥ 在反序列化时,使用 readResolve( )方法。

⑦ 使用static代码块实现单例模式

静态代码块中的代码在使用类的时候就已经开始执行了,可以利用这个特性实现单例设计模式。    

⑧ 使用enum枚举数据类型实现单例模式

枚举 enum 和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,利用这个特性实现单例设计模式。

第七章  拾遗增补

63、线程对象在不同的运行时期有不同的状态,状态信息就存放在 State枚举类中。线程状态有以下几种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING 、TERMINATED,调用与线程有关的方法是造成线程状态改变的主要原因。

NEW:线程实例化后还从未执行start()方法时的状态。

RUNNABLE:线程进入运行的状态。

TERMINATED:线程被销毁时的状态。

TIMED_WAITING:线程执行了Thread.sleep( )方法,呈等待状态,等待时间到达,继续向下运行。

BLOCKED:出现在某一个线程在等待锁的时候。

WAITING:线程执行了Object.wait( )方法后所处的状态。

64、线程组的作用就是:可以批量的管理线程或线程组对象,有效的对线程或线程组对象进行组织。

65、在实例化一个ThreadGroup 线程组x 时如果不指定所属的线程组,则x 线程组自动归到当前线程对象所属的线程组中,也就是隐式地在一个线程组找那个添加了一个子线程组。

66、JVM的根线程组就是system,再取其父线程组则会出现空指针异常。

67、通过将线程归属到线程组中,当调用线程组 ThreadGroup 的 interrupt()方法时,可以将该组中的所有正在运行的线程批量停止。

68、SimpleDateFormate主要负责日期的转换与格式化,但是在多线程的环境中,使用此类容易造成数据转换及处理的不准确,因为SimpleDateFormate类并不是线程安全的。

解决异常方法一是创建多个SimpleDateFormate类的实例。

解决异常方法二是ThreadLocal 类能使线程绑定到指定的对象,使用该类也可以解决该问题。

69、线程中出现异常的处理:在Java多线程技术中,可以对多线程中的异常进行捕捉,使用的是 UncaughtExceptionHandler类,从而对发生的异常进行有效的处理。

方法setUncaughtExceptionHandler( )的作用是对指定的线程对象设置默认的异常处理器。

方法setDefaultUncaughtExceptionHandler( )的作用是对指定的线程类的所有线程对象设置默认的异常处理器。

70、在默认的情况下,线程组中的一个线程出现异常不会影响到其他线程的运行。

参考资料:《Java多线程编程核心技术》

原文地址:https://www.cnblogs.com/Demrystv/p/9164210.html

时间: 2024-10-21 06:39:55

多线程知识点总结(二)的相关文章

多线程知识点(二)

1:互斥锁,只有一个线程进行(例如:读写)操作 2.栈区上面的对象随时可能销毁,ARC下block会自动保存到堆区 //NSGlobalBlock全局区(未使用局部变量) //    void (^task)() = ^{ //        NSLog(@"task"); //    }; //    NSLog(@"%@",task); // //    int a = 6; //    //NSStackBlock  栈区上面的对象随时可能会被销毁 //   

Android多线程分析之二:Thread的实现

Android多线程分析之二:Thread 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前文<Android多线程分析之一:使用Thread异步下载图像>中演示了如何使用 Thread 处理异步事务.示例中这个 Java Thread 类都是位于 Framework 层的类,它自身是通过 JNI 转调 dalvik 里面的 Thread 相关方法实现的.因此要分析 Androd 中的线程,就需要分析这两层中的与线程相关的代码,这就是本文要

琐碎的知识点(二)

1.绑定设置时间格式 <%#Eval("time", "{0:yyyy-MM-dd}")%> <%#DateTime.Parse(Eval("time").ToString()).ToString("yyyy-MM-dd")%> 2.Repeater1.Items.Count 是指Repeter1的数据绑定条数 <%#this.rptnewstraining.Items.Count == 0 ?

.NET知识点总结二(笔记整合)

19.什么是类型? 用来定义某一种数据在内存里开辟空间的大小,还可以预置操作此种类型数据的相关方法 20.this关键字在方法中使用时所代表的含义 this指的是当前类的对象,或者父类的类的对象(base只能指向父类的对象) 21.里氏替换原则 子类替换父类所在的位置 22.C#中的数据类型 值类型 简单类型(数字类型(int,short,long,float,double),字符(char),逻辑值(bool)),结构体(struct),枚举(enum) 引用类型 字符串(string),数组

多线程开发之二 NSOperation

效果如下: ViewController.h 1 #import <UIKit/UIKit.h> 2 3 @interface ViewController : UITableViewController 4 @property (strong, nonatomic) NSArray *arrSampleName; 5 6 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName; 7 8 @end  ViewContro

20150225 IMX257 设备驱动模型之sysfs文件系统知识点整合(二)

20150225 IMX257 设备驱动模型之sysfs文件系统知识点整合(二) 2012-02-25 李海沿 前天我们实现了一个简单的sysfs的kobject的驱动程序,可是有没有发现很多东西都不懂,原因就是在我们对sysfs和kobject的工作原理不懂,虽然我一直不提倡整天接触那些乏味的知识点,也一直不喜欢谈论太多的知识点,但是有的时候,理论知识是实践的基础,有些基础的知识点还是不得不提,下面进入正题. 一.sysfs介绍 在linux2.6内核以后,引入了一个新的文件系统sysfs,它

Android多线程——Handler (二)

传递消息的集中方式: 一: Message message = Message.obtain(); message.obj = data; message.what = IS_FINISHED; handle.sendMessage(message); 二: Message message = Message.obtain(handle); message.obj = data; message.sendToTarget(); 三:可以在message中传递复杂数据 Message messag

Java 多线程知识点整理

1.如何停止一个正在运行的线程? 使用共享变量,用volatile变量修饰,线程循环去访问该变量.如果线程因为发生某些事件进行阻塞,可以使用Thread的interrupt方法.可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码. class MyThread extends Thread { volatile boolean stop = false; public void run() { while (!stop) { System.out.println(g

Java多线程基础(二)定时器类:Timer类和TimerTask类

Java多线程基础(二)定时器类:Timer类和TimerTask类 Timer类和TimerTask类是jdk实现定时器功能的早期方法,jdk1.5以前就支持Timer类和TimerTask类.JDK1.5之后引入了新的机制,将在后续博文中研究. 1 指定时间间隔后执行任务 import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TraditionalTimerTest {

进行概念详解 多线程上篇(二)

操作系统是程序与硬件交互的中间层,现代操作系统将程序的一次执行抽象为进程和线程的概念. 进程作为资源分配的基本单位,线程作为执行的基本单位. 进程和线程其实就是操作系统程序本身实现控制一个程序运行的数据项描述 所有的程序都是面向语言进行开发的,而语言本身是面向操作系统的,线程是操作系统对程序一次运行的抽象 所以,所有的多线程编程模型,必然遵从操作系统的大逻辑,必然是符合操作系统的对线程的抽象概念,操作系统在抽象之上提供了API供应用程序调用 简言之,应用程序的底层是借助于操作系统来完成多线程编程