Linus:The only place where parallelism matters is in graphical or the server side,where we already largely have it. Pushing it anywhere is just pointless
同步和异步
同步:同步调用一旦开始,调用者必须等到方法返回后才能继续后续的行为
异步:异步方法更像一个消息传递,一旦开始,方法调用就会立刻返回,调用者可以继续后续的操作.异步方法通常会在一个线程中真实的执行.如果异步调用需要返回结果,那么当这个调用真实完成时,则会通知调用者.
并发和并行
并发:偏重于多个任务交替执行.
并行:真正意义的同时执行.
通常两者不做区分.
临界区
临界区用来表示一种公共资源或者说共享数据,可以被多个线程使用,但是每一次,只能有一个线程使用它,一旦临界区资源被调用,其他线程想要这个资源只能等待.
例子:打印机
阻塞和非阻塞
如上
死锁,饥饿,和活锁
死锁:
饥饿:某一个或者多个线程因为某种原因无法获得所需要的资源,导致一直无法执行下去.
活锁:如果两个线程的"智力"不够,且都秉持着"谦让"的原则,主动将资源释放给他人使用,那么导致两个资源不断地在两个线程之间跳动,没有一个线程可以同时拿到所有资源正常执行.
原子性:一个操作是不可被中断的
可见性:如果一个线程修改了一个全局变量,那么其他线程马上就可以知道这个改动.
有序性:理解 指令重排
Happen-Before 规则:
线程的定义:
线程的生命周期:线程的所有状态都在 Thread 中的 State 枚举中定义
public enum State{
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
NEW 状态表示刚刚创建的线程,这种线程还没有开始执行.等到线程的 start()方法调用时,才表示线程开始执行,当线程执行时,处于 RUNNABLE 状态,表示线程所需的一切资源都准备好了,如果线程在执行过程遇到了 synchronized 同步块,就会进入 BLOCKED 阻塞状态,这时表示线程会暂停执行,直到获得请求的锁,WAITING 和 TIMED_WAITING都表示等待,TIMED_WAITING 表示一个有时限的等待.等待的事件,比如,通过 wait()方法等待 notify()方法,而通过join() 方法等待的线程则会等待目标线程的终止,一旦等到了期望的事件,线程就会再次执行,进入 RUNNABLE 状态.等线程执行完毕后,则进入 TERMINATED 状态,表示结束
Thread t1 = new Thread();
t1.start;
Thread t1 = new Thread();
t1.run; //不能新建一个线程,而是在当前线程中调用 run 方法,只是作为一个普通的方法调用
Thread t1 = new Thread(){
@Override
public void run(){
System.out.println("Hello I am t1");
}
} //匿名内部类:创建一个继承自 Thread 的匿名类对象
t1.start;
public class CreatThread implements Runnable{
@Overide
public void run(){
System.out.println("Oh,I am Runnable!");
}
publc static void main(String[] args){
Thread t1 = new Thread(new CreateThread());
t1.start();
}
}
为什么弃用 Thread.stop()
Thread.stop()方法在终止线程时,会直接终止线程,并立即释放这个线程所持有的锁,而这些锁恰恰是用来维持对象一致性的.
线程中断
线程中断并不会使线程立即退出,而是给线程发送一个通知,告诉目标线程,有人希望你退出了.至于目标线程接到通知后如何处理,则完全由目标线程自行决定.
public void Thread.interrupt() //中断线程(只是设置中断状态,并不是会停止线程)
public boolean Thread.isIntertupted() //判断是否被中断
public static boolean Thread.interrupted()//判断是否被中断,并清除当前中断状态
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(){
@Override
public void run() {
while (true){
if(Thread.currentThread().isInterrupted()){
System.out.println("被中断了!");
break;
}
try {
Thread.sleep(2000);//由于中断抛异常,此时会清楚中断标记
} catch (InterruptedException e) {
System.out.println("睡眠的时候被中断了!");
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
};
t1.start();
Thread.sleep(2000);
t1.interrupt();
}
}
//输出:睡眠的时候被中断了!
// 被中断了!
等待方法和通知方法并不是 Thread 类中的,而是在 Object 类中,这也意味着任何对象都可以调用这两个方法
在线程 A 中调用了 obj.wait() 方法,那么线程 A 就会停止执行,转为等待状态.线程 A 会一直等待到其他线程调用了 obj.notify()方法为止.这时,obj 显然成了多个线程之间通信的有效手段.
Object.wait()方法不能随便调用.他必须包含在对应的 synchronized 语句中,无论是 wait 方法还是 notify 方法,都必须先获得目标对象的一个监视器
如果一个线程调用了 wait 方法,那么他就会进入 object 对象的等待队列,这个队列中可能有多个线程,当 notify 方法被调用时,他就会从这个等待队列中 随机选择一个线程,并将其唤醒.这个选择是不公平的,完全随机的
注意:Object.wait()方法和 Thread.sleep()方法都可以让线程等待若干时间.除 wait()方法可以被唤醒外,另一个主要区别时 wait()方法会释放目标对象的锁,而 Thread.sleep()方法不会释放任何资源
join():需要等待依赖线程执行完毕,才能继续执行.
public final void join() throws InterruptedException//它会一直阻塞当前线程,直到目标线程执行完毕
public final synchronized void join(long millis) throws InterruptedException//给出了最大等待时间
public static native void yield()//会使当前线程让出 CPU,让出后依然会进行 CPU 资源的争夺
并不能真正保证线程安全,他只能确保一个线程修改了数据之后,其他线程能够看到这个改动.
public class ThreadGroupName implements Runnable{
public static void main(String[] args) {
ThreadGroup tg = new ThreadGroup("PrintGroup");
Thread t1 = new Thread(tg,new ThreadGroupName(),"T1");
Thread t2 = new Thread(tg,new ThreadGroupName(),"T2");
t1.start();
t2.start();
System.out.println(tg.activeCount());
tg.list();
}
@Override
public void run() {
String groupAndName = Thread.currentThread().getThreadGroup().getName()+"-"+Thread.currentThread().getName();
while (true){
System.out.println("I AM " + groupAndName);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
如垃圾回收线程,JIT 线程都可以理解为守护线程.与之相对应的用户线程,用户线程可以理解为是系统的工作线程.如果用户线程全部结束,作为意味着这个程序实际上无事可做了,守护线程要守护的对象已经不存在了,那么整个应用程序就应该结束,因此,当一个 java 应用内只有守护线程时,java 虚拟机就会自然退出
public class DaemonDemo{
public static class DaemonT extends Thread{
@Override
public void run() {
while(true){
System.out.println("I AM ALIVE");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DaemonT());
t1.setDaemon(true);//设置守护线程必须在线程 start()之前设置,否则会出异常
t1.start();
Thread.sleep(2000);
}
}
high.setPriority(Thread.MAX_PRIORITY)//public final static int MIN_PRIORITY = 1
low.setPriority(Thread.MIN_PRIORITY)//10
优先级高的线程在竞争资源时会更有优势,但这依然是个概率问题
对于 ReentrantLock 的几个重要方法,整理如下
public class ReentrantLockCondition implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();//记住怎么关联condition
@Override
public void run() {
try{
lock.lock();
condition.await();//会让当前线程等待,同时释放当前锁,当其他线程使用 signal()方法时,线程会重新获得锁并继续执行,或者当线程中断时,也能跳出等待
System.out.println("GOING ON");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new ReentrantLockCondition());
t1.start();
Thread.sleep(2000);
lock.lock();
condition.signal();
lock.unlock();//如果如果不释放,虽然已经唤醒了线程t1,但是由于他无法重新获得锁,因而无法继续执行
}
}