《实战Java高并发程序设计》学习笔记(二)

本文围绕Java并行程序基础展开,介绍了线程的状态,如WAITING、TIMED_WAITING等,阐述了stop()、suspend()等废弃方法的原因及替代做法,还讲解了线程中断、wait()与sleep()方法区别等知识,提及Java内存模型、线程组、守护线程等,最后分析了并发下ArrayList、HashMap等的常见错误及解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java并行程序基础

线程的所有状态都在Thread中的State枚举中定义:

WAITING:会进入一个无时间限制的等待。正式等待一些特殊的时间,如wait()等notify方法,join()会等待目标线程的终止。

TIMED_WAITING:会进行一个有时限的等待。

 

stop()方法被废弃而不推荐使用的原因:

过于暴力,强行把执行到一般的线程终止,可能会引起一些数据不一致的问题。

需要停止一个线程的简单做法,定义一个标记变量stopme,用于指示线程是否需要退出。

对于线程退出的功能,JDK提供了更强大的支持——线程中断

Thread.interrupt是个实例方法,通知目标线程终端,并设置中断标志位。

Thread.isInterrupted也是个实例方法,判断当前线程是否被中断。

静态方法Thread.interrupted也用来判断当前线程的中断状态,并会清除中断标志位。

中断的功能更为强劲,若在程序中出现了wait()或sleep()这样的操作,只能通过中断来识别了。

Thrad.sleep()方法会由于中断而抛出异常,此时,会清除中断标记。

 

任何对象都可以调用wait()方法和notify()方法

当在一个对象实例上调用wait()方法后,当前线程就会在这个对象的等待队列上等待。

直到其他线程调用了obj.notify(),随机在等待队列上选取一个线程唤醒。notifyAll()会唤醒所有。

对上述两个方法的调用,须包含在对应的synchronized语句中,即需先获取obj目标对象的一个监视器。

Object.wait()和Thread.sleep()方法的区别:

1、wait()方法能被唤醒。

2、wait()方法会释放目标对象的锁,而Thread.sleep()方法不会释放任何资源。

 

被标注为另外两个废弃方法:suspend()方法和resume()方法,配对使用。

被废弃的原因,suspend()方法在导致线程暂停的同时,不释放任何锁资源,直到相应线程上进行了resume()方法。

并且还会发生,resume()方法执行地比suspend()方法早。

在应用层面上实现suspend()方法和resume()方法:

 

一个线程的输入可能非常依赖于另外一个或者多个线程的输出。

在A线程(调用线程)中调用,B.join()方法,则会阻塞A线程,直到B线程执行完毕。

join()方法的本质:

让调用线程wait()方法在当前线程对象实例上。当B执行完后,B线程会在退出前调用notifyAll()方法通知所有的等待线程继续执行。

注意:不要在应用程序中,在Thread对象实例上,使用类似wait()或notify(),因为这可能会影响系统API的工作,或者被系统API所影响。

Thread.yield(),这是个静态方法,一旦执行,会使当前线程让出CPU,但还会进行CPU资源的争夺。

 

Java内存模型(JMM),都是围绕着原子性、有序性和可见性展开的。

确保线程间的原子性、有序性和可见性,可使用特殊操作或关键字,告诉JVM,不要随意变动优化目标指令。

关键字volatile,可使所修饰变量在所有的线程中,看到的数据是一致的。

ps:可使用Java虚拟机参数-server切换到Server模式。

 

线程组:

activeCount()方法可以获得活动线程的总数,但由于线程是动态的,这个值是个估计值。

list()打印这个线程组所有的线程信息。

 

守护线程(Daemon)

它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程。

Thread.setDaemon(true);//在start()前设置

线程优先级

Thread.setPriority(); //在资源竞争时,优先级的差异表现地更为明显

 

线程安全以及synchronized关键字

 

常见错误:

1、并发下的ArrayList,在扩容过程中,内部一致性被破坏,但由于没有锁的保护,另外的线程访问了不一致的内部状态,导致出现越界问题。

2、并发下的HashMap

jps:可以显示当前系统中所有的Java进程

jstack:可以打印给定Java进程的内部线程及其堆栈。

put的方法:

由于所处循环时一个迭代遍历,因为多线程的冲突,导致链表结构被破坏,当链表成环时,上述迭代就是一个死循环。

在JDK8对HashMap内部实现做了大规模调整,规避了这个问题。

解决办法,使用ConcurrentHashMap。

3、错误的加锁

对Integer对象i加锁。但Integer属于不变对象,即对象一旦被创建,就不可能被修改。

当i++时,会创建一个新的Integer对象,从而导致,每次获得的是不同对象的锁。

ps:可使用javap命令反编译代码段!

在i++时,实际是i=Integer.valueOf(i.inteValue()+1);

Integer.valueOf()实际是个工厂方法,因此,i++的本质是,创建一个新的Integer对象,并将它的引用赋值给i。

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值