线程基础学习

来源:互联网 发布:cad珠宝设计软件 编辑:程序博客网 时间:2024/06/06 12:33

github地址:https://github.com/liujinjin777/liujinjintest.git

讲解流程为:线程状态 -》实现线程的几种方法 -》线程基本方法和关键字 -》问答时刻

线程状态

这里写图片描述
图来自:http://blog.csdn.net/huang_xw/article/details/7316354

新建、准备/就绪、运行、阻塞、死亡
阻塞:1,等待阻塞,运行线程调用wait方法进入等待池【wait和sleep,wait会释放锁】
2,同步阻塞,就绪状态线程准备获取锁的时候,已经有其他线程占领了,这个时候就进入锁池中。
3,其他阻塞,运行线程调用sleep或者join或者io请求,进入阻塞状态。当sleep结束join线程结束io处理完毕,线程重新进入就绪状态。

Thread类源码状态

public enum State {    NEW,               // 新建    RUNNABLE,          // 运行    BLOCKED,           // 锁阻塞               WAITING,           // 等待阻塞(不带有超时值)    TIMED_WAITING,     // 等待阻塞(带有超时值)    TERMINATED;        // 终止}

实现线程的三种方法

其实按照原理来分的话是2种,实现runnable或者callable
1.实现runnable接口,实现run方法。

Thread thread = new Thread(Runable runable);thread.start();

代码实现:把runnable记录成为Thread的成员变量target 。调用run的时候,直接调用

@Overridepublic void run() {    if (target != null) {        target.run();    }}

2.继承Thread类,重写run方法。

Thread myThread = new MyThread();myThread.start();

3.利用callable接口,重写call方法。

public class MyCallable implements Callable {    private int flag = 0;    public MyCallable(int flag){        this.flag = flag;    }    public String call() throws Exception{        if (this.flag == 0){            System.out.println("call flag = 1");            return "flag = 0";        }        if (this.flag == 1){            try {                while (true) {                    System.out.println("looping.");                    Thread.sleep(2000);                }            } catch (InterruptedException e) {                System.out.println("Interrupted");            }            return "false";        } else {            System.out.println("Bad flag value!");            throw new Exception("Bad flag value!");        }    }}
//-------- 第1种写法,类似重写runable接口MyCallable task1 = new MyCallable(0);MyCallable task2 = new MyCallable(2);FutureTask<MyCallable> oneTask1 = new FutureTask<MyCallable>(task1);FutureTask<MyCallable> oneTask2 = new FutureTask<MyCallable>(task2);Thread thread = new Thread(oneTask1);Thread thread2 = new Thread(oneTask2);thread.start();thread2.start();//-------- 第2种MyCallable task1 = new MyCallable(0);MyCallable task2 = new MyCallable(1);MyCallable task3 = new MyCallable(2);ExecutorService es = Executors.newFixedThreadPool(3);try {    // 提交并执行任务,任务启动时返回了一个Future对象,    // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作    Future future1 = es.submit(task1);    // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行    System.out.println("task1: " + future1.get());    Future future2 = es.submit(task2);    // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环    Thread.sleep(5000);    System.out.println("task2 cancel: " + future2.cancel(true));    // 获取第三个任务的输出,因为执行第三个任务会引起异常    // 所以下面的语句将引起异常的抛出    Future future3 = es.submit(task3);    System.out.println("task3: " + future3.get());} catch (Exception e){    System.out.println(e.toString());}// 停止任务执行服务es.shutdownNow();

callable与runable的差别
1,重写call方法和重写run方法
2,可以有返回值
3,可以抛出异常

线程基本方法和关键字

start、sleep、join 、yeild、run、wait、notify、notifyAll、interrupt、synchronized、volatile

start:启动一个线程。会调用run方法。
sleep:睡眠一个线程。睡眠期间不会释放锁。【com.liujinjin.myThread.testSleep】
join:当前线程会等待调用join线程运行后运行。【com.liujinjin.myThread.testJoin】
yeid:让步线程。尽可能(不绝对)让优先级高的执行 。【com.liujinjin.myThread.testYield】
每个线程都有自己的执行级别,范围是1-10,默认为5。通过setPriority方法更改。
run:与start配合使用,如果直接调用和普通方法是一样的。
wait : 等待线程。等待期间释放锁,调用前需要获得调用对象锁。【com.liujinjin.myThread.testWait】
notify : 唤醒线程。与wait方法联合使用,调用前需要获得对象锁。调用后不释放锁。【com.liujinjin.myThread.testnotify2】
notifyAll :与notify类似,唤醒所有wait线程。
interrupt : 不会中断一个线程。而是当线程被阻塞(wait,join,sleep)时,一种停止阻塞的方法。【com.liujinjin.myThread.testInterrupt】


Object:wait、notify、notifyAll
Thread:start、sleep、join 、yeild 、interrupt
Runnale:run
关键字:synchronized、volatile

native修饰:wait、notify、notifyAll、start0、sleep、yeild、interrupt0
非native修饰:join、run
synchronized修饰:start、join
static修饰:sleep

问答

1.什么时候需要考虑线程安全问题?
当使用变量为成员变量的时候。方法内定义的变量无线程问题。
2.线程可以重复启动吗?
不可以,在start的时候会检查线程状态,如果不为0会 throw IllegalThreadStateException。
2.1那么线程池是如何做的?
待调研
3.线程的底层是通过cpu时间分片来实现的,串行化随机分给每个线程时间片。为什么这样会比单线程快?
4.线程的优点和缺点
5.volitle为何不能保证线程安全?
其底层实现原理,在汇编语言加了lock前缀,实现的功能是
1)在处理器中运算的变量改变后,立刻刷回主线程。
2)主线程里的变量被修改之后,失效其他处理器内的变量值。
一个加法操作。
1)从主线程读取变量值到处理器。
2)更改值。
3)刷回主线程。
6.为什么wait、notify、notifyAll等方法放在object类里,而不是thread类里?
1,wait、notify是sychnized修饰的,调用的前提是获取到当前对象的锁,而锁是这个对象本身。如果是放在thread里,每个对象的锁是当前的thread对象,其他线程调用的时候需要先获取到之前wait线程对象锁。不直观,复杂。
2,可以让object类和子类使用成为锁。