1. 临界区 : 就是线程对象共享的区域
2. 原子性: 在java中 比如为一个变量赋值,不论几个线程以何种方式何种步调为其赋值,变量在某一刻的值总会是这几个线程赋的值之一,线程之间是不会互相干扰的,不可中断的.
但是有例外:比如在32位的虚拟机来说,由于long有64位, 也就是说假如有两个线程对其进行赋值,就会发生问题. 即在32位虚拟机中对long的读写都不是原子性的了,线程间会干扰.
3. 可见性: 指的是当一个线程修改了某一个共享变量的值,别的线程能否立即知晓这个修改
4. 有序性. 指令重排是为了提升cpu效率性能,但在java虚拟机的执行系统中,指令重排是有一些规则的(happen-before)
java内存模型(JMM)都是围绕原子性 有序性 可见性展开的
5. 线程状态
6. Thread t1 = new Thread(); t1.run();
注意:不要像上面一样使用run()来开启新线程,他只会在当前线程中串行的执行run()中的代码
而应该使用start()
7. 为什么jdk现在把thread.stop()方法废弃,因为这样终止线程太过于暴力,在单线程中不会出现问题,但在并行程序中,调用后立即终止线程,该线程所持有的锁会立即释放,导致数据不一致等线程安全问题,在庞大的项目面前,这种问题难于排查,也不会报错
8. 那如何正确停止一个线程:
一: 只要我们自行决定线程在何时退出即可,比如线程中是一个无限循环,设置一个标记变量,true后break 停止循环,线程执行完毕后会自己终止
二:
interrupt()只是设置中断标志位,并不会真的中断线程, 而需要我们通过这个中断标记位(isInterrupted())来设置中断处理代码, 即类似于上面我们自己实现的标记变量的方法,但这种方式有更厉害之处: 如果在循环体中,出现了类似wait或者sleep这样的操作,则只能通过这种方式来实现
需要注意的是:在11行处,因为调用了interrupt()抛出异常会进入13行,捕获到中断,在此处可立即退出线程,但为了保证数据完整和一致性,在15行再次中断自己,重新置上中断标记位(sleep中断而抛出异常后,会清除标记位),这样在第6行中断检查中,安全中断.
9. wait和notify (两个都是Object类的方法)
当在一个对象实例(即锁)上调用wait()后,当前线程就会在该对象上等待,进入这个对象的等待队列中,等待队列中可能会有多个线程(多个线程wait于该对象),直到其它线程调用了该对象.notify(),他就会从等待队列中随机选择一个线程将其唤醒. notifyAll()是唤醒全部. 被唤醒的线程并不能立即执行后续的代码,而是需要重新获得obj的监视器后才能继续,否则还得等待.(唤醒它的线程的synchronzied代码块执行完后才会放出obj)
注意:Object.wait()方法调用必须包含在其对应的synchronzied语句中
wait()会释放锁,可被唤醒 ; sleep不会释放任何资源
10. threadAAA.join() 不带参数的表示会一直阻塞调用线程,直到 threadAAA 线程执行完毕. 带参数的表示最大等待时间,如果超过给定时间还没执行完,调用线程会"等不及"而继续执行了.
join()的本质是让调用线程wait()在当前线程对象实例上. 当 threadAAA 执行完毕后会在退出前调用notityAll()通知所有的等待线程继续执行. 故尽量不要在Thread对象实例上使用类似wait()或者notity()等方法(Object的方法),会和系统ap互相影响.
11. volatile : 保证变量原子性(32位虚拟机读long)和可见性(循环判断bool值,bool值某时变化). 它不能带替锁,因为它不能保证复合操作的原子性.
12. synchronized : 基本三种用法
a. 代码块 指定加锁对象,锁是一个给定对象(比如在外面new一个object)
b. 直接作用于实例方法(即非静态方法),相当于锁是这个方法的类的实例. 锁必须为同一个实例
c . 直接作用于静态方法,相当于锁为这个方法的类. 只要同为这个类的实例即可