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。