一个程序运行后被抽象为一个进程;
一个程序至少有一个进程,一个进程至少有一个线程.
线程是程序执行时的最小单位,是CPU调度和分派的基本单位;
它是进程的一个执行流,一个进程可以由很多个线程组成; 线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量;
线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行
 
 线程由阻塞状态,只能进入到就绪状态,而不能直接变为运行状态
单核CPU,同一时间只能有一个线程运行,不同系统由不同的策略分配给各个线程执行时间;
多核CPU,同时可有多个线程并行(不是并发)
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪,notify通知);
以下情况会发生阻塞:
sleep
调用IO方法等待返回
等待获取资源监视器monitor(synchronize)
wait等待获取notify通知
调用suspend()将线程挂起(容易死锁,避免使用)
yield(不会阻塞,直接由运行状态变为就绪状态)
new MyThread().start(); 复制代码
new Thread(new MyRunnable()).start(); 复制代码
暂停执行一段时间,进入阻塞状态;
时间过去后,进入就绪状态
把指定的线程加入到当前线程,让一个线程等待另一个线程执行完以后再执行,可以将两个交替执行的线程合并为顺序执行的线程
...
        try {
            threadA.start();
            threadA.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //当前线程等待threadA线程执行完以后,再往下执行
        ...
复制代码 
 线程让步:暂时让出自己的时间片给通优先级线程
当前线程由执行状态,转入就绪状态,不会阻塞线程
只是转入就绪状态,让线程调度器重新调度一次:
与当前线程优先级相同或更高,且处于就绪状态的线程获取执行,进入运行状态。 也有可能yield以后调度器重新立刻执行该线程。
Daemon Thread,又称“守护线程”,在后台运行,为其它线程提供服务;
所有前台线程死亡,后台线程自动死亡;
setDaemon(true),在start()之前设置 isDaemon()判断是否为后台线程 复制代码
mThread.setPriority(Thread.MAX_PRIORITY);//10
    mThread.setPriority(Thread.NORM_PRIORITY);//5
    mThread.setPriority(Thread.MIN_PRIORITY);//1
    可以直接写数字,但是不同操作系统优先级不同,也不能很好的对应Java的10个优先级
    所以,尽量使用Thread提供的优先级静态字段。
复制代码 
 Thread mThread = new Thread() {
    public void run() {
        if (mThtread.isInterrupted()) {
            //收尾
            return;
        }
    }
};
mThread.start();
mThread.stop();//强行终止
mThread.interrupt();//标记,配合终止
复制代码 
 仅仅返回当前状态
返回当前状态,并重置该状态
The interrupted status of the thread is cleared by this method
mThread.interrupt();//标记,配合终止
Thread mThread = new Thread() {
    public void run() {
    
        //只返回状态
        if (mThread.isInterrupted()) {
            //收尾
            return;
        }
        
        //返回状态,并重置状态
        if (Thread.interrupted()) {
            //收尾
            return;
        }
        try {
            Thread.sleep(2000);
            //interrupt()时,能唤醒sleep、join、wait
            //内部使用Thread.interrupted()检查、重置、抛异常
        } catch (InterruptedException e) {
            e.printStackTrace();
            //收尾
            return;
        }
        //被打断,不会重置状态,内部实现里又重新设置回来了
        SystemClock.sleep(2000);
    }
};
总结:
在mThread.isInterrupted()、Thread.interrupted()、InterruptedException中做收尾工作
复制代码 
 方法、代码块
保证方法或代码块内部对资源的互斥访问;
即同一时间,同一个monitor监视的代码,最多只能有一个线程访问
 
 保证线程之间,对监视资源的数据同步;
即任何线程在获得monitor的第一时间,现将共享内存中的数据复制到自己的缓存中;
任何线程在释放monitor的第一时间,将自己缓存中的数据复制到共享内存中
 
 同样是“加锁”机制,使用方式更灵活,但也更麻烦
Java 并发:Lock 框架详解
读写锁...
程序的最小执行单位,不能被分割执行,如 a = 1+2;
注意:i++不是原子操作 --> tmp = i+1; i = tmp
轻量级的synchronize
作用和volatile基本一致,对于操作基本类型做了封装,更方便;
AtomicInteger、AtomicBoolean...
AtomicInteger ato = new AtomicInteger(); ato.addAndGet(1); ato.getAndAdd(2); 复制代码
无论是线程安全问题,还是衍生出的锁机制问题,它们的核心都在于“保证资源的安全性”,而不是某个方法或某几行代码
数据的安全性
多个线程访问共同的资源时,在某一个线程对资源进行写操作的中途,其它线程对该资源进行读、写,导致出现了数据的错误。
对资源进行访问限制
使同一时间只有一个线程可以访问资源,保证数据的准确性。
线程释放共享资源锁,进入等待队列,直到被再次唤醒
唤醒该共享资源锁上等待的单个/全部线程,唤醒哪个和顺序:取决于优先级和JVM
private String testStr;
public void setStr(String str) {
    synchronized (obj){
        this.testStr = str;
        obj.notify();
    }
}
public String getStr() {
    /**
     为什么不用if而要用while?
     因为wait不确定会被谁唤醒(notify、interrt)
     1. wait()一般都是这样搭配的标准写法
     2. wait不只会被notify唤醒,还可能会被intrupt唤醒;
        此时如果是if的话,wait被唤醒,就不查是否为null直接return了
     */
    synchronized (obj){
        //if (testStr == null){
        while (testStr == null) {
            try {
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return testStr;
        }
    }
}
    
#范式
    等待方:
    synchronized( 对象 ) {
        //not if
        while(条件) {
            对象.wait();
        }
        对应的处理逻辑
    }
    
    通知方:
    synchronized( 对象 ) {
        改变条件;
        对象.notifyAll();
    }
复制代码