首页 > 编程知识 正文

java变量类型(java多线程详解)

时间:2023-05-06 20:14:04 阅读:94899 作者:2898

上一节介绍了Thread的活动计数(和枚举) thread [ ] thd阵列(array )方法。 enumerate(thread[]thdarray )只是以下四种枚举方法之一:

1.int enumerate (thread [ ] thdarray )被复制到thd array的当前线程组和所有子组中每个活动线程的引用中。

2.int enumerate (thread [ ] thdarray,布尔阵列) thd array仅在recurse为false时复制对当前线程组中每个活动线程的引用否则,此方法将包含来自子组的活动线程。

3.int enumerate (thread group [ ] TGarray )可复制对TG array当前线程组中每个活动子组的引用。

4.int enumerate (thread group [ ] TGarray,布尔注册表) TG array仅当recurse为false时,才复制对当前线程组中每个活动子组的引用否则,该方法将包括活动子组的所有活动子组、活动子组的活动子组等。

可以使用thread组' s '活动计数和枚举[ ] thd阵列方法枚举所有程序线程。 首先,找到系统线程组。 然后,调用ThreadGroup的activeCount ()方法以获取活动线程计数数组的上浆目的。 然后,调用thread组的枚举(thread [ ] thd array )方法来填充数组的thread引用,如实例4所示。

运行时,EnumThreads将生成以下输出:

除了主线程以外的所有线程都属于系统线程组。

波动性

的可变性或变性表示一个线程更改了共享字段变量的值,而另一个线程看到了该更改。 其他线程总是想显示共享字段变量的值,但未必如此。 出于性能原因,Java不需要JVM实现从主内存或对象堆内存中读取值,也不需要将值写入共享字段变量。 相反,JVM可能会从处理器寄存器或缓存读取共享字段变量的值。 这统称为工作存储器。 同样,JVM有时会将共享字段变量的值写入处理器寄存器或缓存。 此功能影响线程共享字段变量的方式。

假设程序创建共享整数字段变量,x的主存储器的初始值为10。 这个程序启动两个线程。一个线程写x,另一个读x的值。 最后,该程序在JVM实现中运行。 在这个实现中,每个线程都分配了自己的专用工作内存。 换句话说,每个线程都有自己的专用复制副本x。 如果写入线程的写入x为6,则写入线程只更新专用工作存储器的副本x; 线程不更新主存储器副本。 此外,读取线程在读取时返回x的值来自读取线程的专用副本。 因此,读线程返回10而不是6。 由于共享字段变量的专用工作存储器副本被初始化为取与自主存储器相对应的值,所以一个线程不知道另一个线程更改了共享字段变量。

如果线程无法观察到其他线程对共享字段变量的更改,则可能会发生严重的问题。 例如,当落寞的母鸡奔跑时,YieldDemo可能会注意到程序最终结束了。 也就是说,JVM实现主存储器的读取/写入而不是工作存储器。 但是,如果发现程序未完成,则主线程可能会将该工作内存的副本设置为“完成”,而不是等效的主内存副本。 另外,YieldDemo线程读取自己的工作存储器的副本finished,不知道实际情况。

要纠正YieldDemo的可见性问题,volatile可以在finished和sum声明中包含Java关键字。 staticvolatilebooleanfinished=false; 和静态电压和=0; 的volatile关键字可确保在线程写入易失性共享字段变量时,JVM修改主存储的副本,而不是线程的工作记忆副本。 同样,JVM会确保始终从主内存副本中读取线程。

注意:在volatile和final中,不能使用共享字段变量将关键字一起声明。 如果尝试使用两个关键字,编译器将报告错误。

线程使用同步访问共享字段变量时,不会出现可见性问题。 新的开发者有时认为波动性取代了同步。 使用volatile (关键字volatile ),可以在同步上下文之外为长整数或双精度浮点共享字段变量赋值,但波动率不能取代同步。 同步允许将多个操作分组到一个不可分割的单元中

中,而这与波动性无关。但是,由于波动性比同步更快,因此在多个线程必须通过单个共享字段变量进行通信的情况下使用波动率。

线程局部变量

Sun的Java 2平台标准版(J2SE)SDK 1.2引入了java.lang.ThreadLocal该类,开发人员使用该类创建线程局部变量 - ThreadLocal基于每个线程存储值的对象。每个ThreadLocal对象为访问该对象的每个线程维护一个单独的值(例如用户ID)。此外,线程操纵自己的值,不能访问同一线程局部变量中的其他值。

ThreadLocal 有三种方法:

1. Object get ():从线程局部变量返回调用线程的值。因为此方法是线程安全的,所以您可以get()从外部调用同步上下文。

2. Object initialValue ():从线程局部变量返回调用线程的初始值。每个线程首次调用get()或者set(Object value)导致间接调用initialValue()以初始化线程局部变量中该线程的值。因为返回null ThreadLocal的默认实现initialValue(),所以必须子类化ThreadLocal并重写此方法以返回非空的初始值。

3. void set (Object value):将线程局部变量中的当前线程值设置为value。使用此方法替换initialValue()返回的值。

实例5演示了如何使用ThreadLocal:

ThreadLocalDemo1创建一个线程局部变量。该变量将唯一的序列号与通过调用访问线程局部变量的每个线程相关联tl.get ()。当一个线程第一次调用时tl.get (),ThreadLocal's get()方法调用initialValue()匿名ThreadLocal子类中的重写方法。以下输出来自一个程序调用:

输入的每个线程名具有唯一的序列号。

如果再次运行此程序,您可能会看到与线程名称关联的其他序列号。虽然数字不同,但它始终与单个线程名称相关联。

计时器

程序偶尔需要一个定时器机制来执行一次或定期执行代码,并在某个指定时间或在一段时间间隔之后执行。在Sun发布J2SE 1.3之前,开发人员要么创建了自定义计时器机制,要么依赖于另一个机制。不兼容的计时器机制导致难以维护的源代码。认识到需要标准化计时器机制,Sun在SDK 1.3中引入了两个计时器类:java.util.Timer和java.util.TimerTask。

根据SDK,使用Timer对象来安排任务 - TimerTask以及子类对象 - 以便执行。该执行依赖于与该Timer对象关联的线程。要创建Timer对象,请调用Timer()或Timer(boolean isDaemon)构造函数。构造函数在创建执行任务时Timer()创建的线程不同:创建非Timer(boolean isDaemon)守护程序线程,而在isDaemon包含true 时创建守护程序线程。以下代码演示了两个构造函数创建Timer对象:

创建Timer对象后,需要TimerTask执行。

既然你有一个Timer对象和一个TimerTask子类,为了TimerTask一次性或重复执行一个对象,请调用以下Timer四种schedule()方法之一:

1. void schedule(TimerTask task, Date time):task在指定的时间执行一次性计划time。

2. void schedule(TimerTask task, Date firstTime, long interval):以下task指定的间隔firstTime和interval毫秒间隔重复执行的计划firstTime。此执行称为固定延迟执行,因为每次后续task执行都相对于先前task执行的实际执行时间发生。此外,如果由于垃圾收集或某些其他后台活动导致执行延迟,则所有后续执行也会延迟。

3. void schedule(TimerTask task, long delay):task在delay毫秒通过后进行一次性执行的计划。

4. void schedule(TimerTask task, long delay, long interval):task在delay毫秒通过后以及以interval毫秒为间隔重复执行的计划firstTime。该方法采用固定延迟执行。

假定一个t1引用Timer对象的下面的代码片段创建一个MyTask对象,并使用上面列表中的第四个方法调度该对象,以便在零毫秒的初始延迟之后重复执行

每秒(即1,000毫秒),该Timer线程执行MyTask的run()方法。有关任务执行的更有用示例,请查看实例8:

Clock1创建一个Timer对象并调用schedule(TimerTask task, long delay, long interval)来使TimerTask子类对象run()方法的固定延迟执行。该方法通过调用获取当前日期java.util.Date的Date()构造函数和转换Date对象的内容。以下部分输出显示运行此程序的结果:

除了这四种schedule()方法外,Timer还包括两种scheduleAtFixedRate()方法:

1. void scheduleAtFixedRate(TimerTask task, Date firstTime, long interval):以下task指定的间隔firstTime和interval毫秒间隔重复执行的计划firstTime。此执行称为固定速率执行,因为每次后续task执行都是相对于初始task执行发生的。此外,如果由于垃圾收集或一些其他后台活动而导致执行延迟,则快速连续发生两次或更多次执行以维持执行频率。

2. void scheduleAtFixedRate(TimerTask task, long delay, long interval):task在delay毫秒通过后以及以interval毫秒为间隔重复执行的计划firstTime。该方法采用固定速率执行。

总结

通过探索线程组,波动率,线程局部变量,定时器和ThreadDeath,掌握使用线程组对相关线程进行分组,使用使用volatile来允许线程访问共享字段变量的主内存副本,使用线程局部变量为线程提供自己独立初始化的值,使用定时器来安排执行任务周期性地或一次性执行,并ThreadDeath用于让线程过早地退出其run()方法。

如有不正确的望指正!有任何JAVA学习问题的可以随时找我.

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。