如果要问我Java当中最难的部分是什么?最有意思的部分是什么?最多人讨论的部分是什么?那我会毫不犹豫地说:多线程。

在了解多线程之前,最好先知道什么是并发,什么是并行。不然很容易迷糊。

总的来说,就是这样:


学习多线程最好从如下六个方面循序渐进(纯粹个人经验和建议,可无视):


Java多线程用一句话总结就是「6类5法」。

所谓「6类」,就是多状态的状态分为这6类:

这是线程生命周期的状态变化图:

Java多线程-线程生命周期(一)-LMLPHP

 

 

 简单来说,就是这样:

Java多线程-线程生命周期(一)-LMLPHP

 

 

 

而所谓「5法」就是线程的核心方法是这么5个:


 

用代码来解释一下会更直观一些。

 

第一种wait/notify的情况:

public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized ("锁") {
                System.out.println("t1 start");
                try {
                    // t1释放锁
                    "锁".wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end");
            }
        }
    });
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized ("锁") {
                System.out.println("t2 start");
                try {
                    // 通知t1进入等待队列
                    "锁".notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("t2 end");
            }
        }
    });

    t1.start();
    t2.start();
}

此时代码执行流程(两种可能):

这里要强调的重点是:

如果说只有两个线程的时候,还能尝试着分析一下结果,那么当有四个线程的时候会如何呢?看看代码:

public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized ("锁") {
                System.out.println("t1 start");
                try {
                    // t1释放锁
                    "锁".wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end");
            }
        }
    });
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized ("锁") {
                try {
                    System.out.println("t2 start");
                    // 随机通知一个等待的线程进入等待队列
                    "锁".notify();
                    System.out.println("t2 end");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread t3 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized ("锁") {
                try {
                    System.out.println("t3 start");
                    // 随机通知一个等待的线程进入等待队列
                    "锁".notify();
                    System.out.println("t3 end");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    });
    Thread t4 = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized ("锁") {
                try {
                    System.out.println("t4 start");
                    // t4释放锁
                    "锁".wait();
                    System.out.println("t4 end");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    });

    t1.start();
    t2.start();
    t3.start();
    t4.start();
}

然后同时开启这四个线程,但结果是无法预料!为什么?因为只有两种可能的流程(要么wait先执行完,要么notify先执行完),至于每种流程里面怎么执行的?不知道!不清楚!无法预料!这就是多线程让人困惑的地方和魅力所在。

而且线程还有一个无赖的行为就是:虽然你有优先级,但我不保证有用!

public class MyThread extends Thread {
    MyThread(String s) {
        super(s);
    }

    @Override
    public void run() {
        for (int i = 0; i <= 10; i++) {
            System.out.println(getName() + " : " + i);
            if (i == 5) {
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程启动");
        Thread t1 = new MyThread("t1");
        Thread t2 = new MyThread("t2");
        t1.setPriority(Thread.MIN_PRIORITY);
        t1.start();
        t2.setPriority(Thread.MAX_PRIORITY);
        t2.start();
        t1.join();
        t2.join();
        System.out.println("主线程结束");
    }
}

这里不管怎么设置t1或者t2的优先级,都没有用,运行的结果每次都可能不一样。

线程的生命周期6类5法算是比较简单的,是基础中的基础。但是用好很难,关键在于多练多想,多多尝试各种组合。

 

11-01 11:51