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的时候,直接调用
@Override
public 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 oneTask1 = new FutureTask(task1);
FutureTask oneTask2 = new FutureTask(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类和子类使用成为锁。