【前言】:
多线程、JVM、操作系统。
【概述】:
基础概念
JUC同步工具
同步容器
Disruptor //一个MQ框架,公认的单机环境下效率最高。
线程池
【线程的概念】:
【纤程】:
【 run和start的区别 】:
//new T1().run(); //方法调用。
new T1().start(); //Thread类里面有start方法。
run方法还是依次顺序执行;( 先run后main , 相当于只有一条执行路径 )
但start方法是分支执行。( 有分支路径 )
【 线程的启动方式 】:
package Ten_Class.t01;
import java.util.concurrent.*;
public class T02_HowToCreateThread {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello MyThread!");
}
}
static class MyRun implements Runnable {
@Override
public void run() {
System.out.println("Hello MyRun!");
}
}
static class MyCall implements Callable<String> {
@Override
public String call() {
System.out.println("Hello MyCall");
return "success";
}
}
//启动线程的5种方式
public static void main(String[] args) throws Exception {
new MyThread().start();
new Thread(new MyRun()).start();
new Thread(() -> {
System.out.println("Hello Lambda!");
}).start();
FutureTask<String> task = new FutureTask<>(new MyCall());
Thread t = new Thread(task);
t.start();
System.out.println(task.get());
ExecutorService service = Executors.newCachedThreadPool();
service.execute(() -> {
System.out.println("Hello ThreadPool");
});
Future<String> f = service.submit(new MyCall());
String s = f.get();
System.out.println(s);
service.shutdown();
}
}
【 线程的方法 】:
【 sleep 】:
睡眠 ,当前线程暂停一段时间, 让给别的线程去运行。
【 yield 】:
我从CPU上先离开 , 进入到一个等待队列里。当然有可能刚进去就被拽出去执行 , 但更大的可能是——将等待队列中其他的拽出去执行。
我让出一下CPU , 然后从等待队列里拽出一个来执行。
【 Join 】:
两个线程 t1 、 t2 , 如果在t1线程的某个点上调用 t2.join , 这个点的意思是——立即跑到t2去运行,t1先等着,什么时候t2运行完了,t1再运行 。
//Join常用来用于等待另一个线程的结束。
【如何保证三个线程按照顺序执行完呢?】:
main{
t1.join();
t2.join();
t3.join();
}
或者
t1{
t2.join();
}
t2{
t3.join();
}
【 线程的状态 】:
【NEW】:
//当我们新建了一个线程,new了一个,还没有调用start( ) 方法之前的线程的状态。
【 Runnable 】:
//当你调用start方法之后 , 它会被线程调度器来执行,也就是交给操作系统来执行了。交给操作系统来执行的话,整个的状态叫做——Runnable。Runnable内部又有两个状态( Ready就绪状态 和 Running运行状态 ) , Ready就绪状态是指我们扔到CPU的等待队列里去了。真正的扔到CPU上去运行了——这个状态叫做Running。
Teminated状态后,不能再回到NEW状态再调用start()了,这样是不行的。
【什么时候进入阻塞?】:
加synchronized进入同步代码块,我这段代码写了synchronized , 但是我还没有得到那把锁 。
【 哪些是操作系统管理的?哪些是JVM管理的? 】:
全是JVM管理的 ,因为JVM管理这些状态的时候也要通过操作系统。JVM 和 操作系统 它俩分不开 , JVM是跑在操作系统上的程序。
【锁的概念】:
package Ten_Class.t01;
public class T_114_01 {
private int count = 10;
private Object o = new Object();
public void m(){
synchronized ( o ){
count--;
System.out.println( Thread.currentThread().getName() + " count = " +count );
}
}
}
【 锁的特性 】:
如果我们每一次都定义一个锁的对象( Object o )的话,这样的加锁方式太麻烦,每次都得New一个新的对象出来。
//所以有一个最简单的方式:
synchronized (this){ //锁定当前方法。
。。。。。。
}
【Class文件】:
每一个class文件load到内存之后,它会专门生成一个class类的对象——和load到内存的那一段代码相对应;
【 方法上加sync 】:
package Ten_Class.t01;
public class T_115_02 {
private static int count = 10;
public synchronized static void m(){ //这里等同于synchronized (T_115_02.class)
count--;
System.out.println( Thread.currentThread().getName() + " count = " + count );
}
}
【 设计小程序验证锁的问题 】:
【规则】:
加了synchronized就没有必要再加volatile了,因为synchronized既保证了原子性又保证了可见性。
【同步方法和非同步方法是否可以同时调用?】:
//下面程序段中m1是同步方法,m2是非同步方法。
/**
* 对比前一个小程序,分析一下这个程序的输出
*/
package Ten_Class.t01;
public class T_116_02 {
private int count = 10;
public synchronized void m1() {
System.out.println( Thread.currentThread().getName() + " m1 start...... " );
try{
Thread.sleep(10000 );
}catch( InterruptedException e ){
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName() + " m1 end 。。。。。" );
}
public void m2(){
try{
Thread.sleep(5000 );
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println( Thread.currentThread().getName() + " m2 " );
}
public static void main(String[] args) {
T_116_02 t = new T_116_02();
new Thread( t::m1 , "t1" ).start();
new Thread( t::m2 , "t2" ).start();
}
}
//说明是允许被同时调用的。
【模拟银行账户 】:
//是否允许客户读取那些不好的数据。
【允许客户读到不好数据】:
写方法加锁,读方法可以不加锁。
【不允许客户读取不好的数据】:
写、读 方法都加上锁。
/**
* 面试题:模拟银行账户
* 对业务写方法加锁
* 对业务读方法不加锁
* 这样行不行?
* <p>
* 容易产生脏读问题(dirtyRead)
*/
package Ten_Class.t01;
import java.util.concurrent.TimeUnit;
/*
* 【面试题】————模拟银行账户
* 对业务写方法加锁;
* 对业务读方法不加锁;
* 这样行不行?
*
* 容易产生脏读问题( dirtyRead )
* */
public class T_116_03_Account {
String name;
double balance;
public synchronized void set(String name, double balance) {
this.name = name;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.balance = balance;
}
public /*synchronized*/ double getBalance(String name) {
return this.balance;
}
public static void main(String[] args) {
T_116_03_Account a = new T_116_03_Account();
new Thread(() -> a.set("zhangsan", 100.0)).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(a.getBalance("zhangsan"));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(a.getBalance("zhangsan"));
}
}
【 加锁与否 】:
能不加锁就不加锁 , 加完锁后的效率要低100倍。
【锁的可重入属性】:
synchronized必须是可重入锁;
【例一】:
package Ten_Class.t01;
import java.util.concurrent.TimeUnit;
/*
* 一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁。
* 也就是说synchronized获得的锁是可重入的。
* */
public class T_117_01 {
synchronized void m1(){
System.out.println("m1 start");
try{
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e ){
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2(){
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e ){
e.printStackTrace();
}
System.out.println("----m2----");
}
public static void main(String[] args) {
new T_117_01().m1();
}
}
【例二】:
package Ten_Class.t01;
import java.util.concurrent.TimeUnit;
public class T_117_02 {
synchronized void m(){
System.out.println("m start");
try{
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("m end");
}
}
class TT extends T_117_02{
synchronized void m(){
System.out.println("child m start");
super.m();
System.out.println("child m end");
}
}
【异常】:
//抛出异常,释放锁,其它想要拿到这把锁的程序会进来。
package Ten_Class.t01;
import java.util.concurrent.TimeUnit;
/*
* 程序在执行过程中,如果出现异常,默认情况锁会被释放;
* 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况;
* 比如,在一个 web app 处理过程中 , 多个servlet线程共同访问一个资源,这时如果异常处理不合适,
* 在第一个线程中抛出异常,其它线程就会进入同步代码区,有可能会访问到异常产生时的数据。
* 因此要非常小心的处理同步业务逻辑中的异常。
* */
public class T_118_01 {
int count = 0;
synchronized void m(){
System.out.println( Thread.currentThread().getName() + " start " );
while (true){
count++;
System.out.println( Thread.currentThread().getName() + " count = " + count );
try{
TimeUnit.SECONDS.sleep(1);
}catch ( InterruptedException e ){
e.printStackTrace();
}
if (count==5){
int i = 1/0; //此处抛出异常 , 锁将要被释放,要想不被释放,可以在这里进行catch , 然后让循环继续。
System.out.println( i );
}
}
}
public static void main(String[] args) {
T_118_01 t = new T_118_01();
Runnable r = new Runnable() {
@Override
public void run() {
t.m();
}
};
new Thread( r,"t1").start();
try{
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
new Thread(r,"t2").start();
}
}