线程(Thread)
Before
程序/进程/线程 之间的关系:
- 程序
程序是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的有序集合。
- 进程
进程是程序的一次执行过程。
- 线程
线程是进程的一个实体。
线程也具有动态性(也具备生命周期),并发性,拥有自己的线程控制块TCB,一个线程可创建另一个线程(例如java主方法创建多个thread线程对象),在同一进程内各线程共享同一地址空间(即所属进程的内存空间),一个进程的线程在另一进程内不可见。
相对进程而言,线程是一个更加接近于执行体的概念,进程在执行过程中拥有独立的内存单元,而线程自己基本上不拥有系统资源,也没有自己的地址空间,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和线程栈)。
ps:进程与线程的关系有点类型于公司与员工的关系;
Why
目前的处理器核心越来越多,使用多线程能有更快的响应速度;
What
线程状态状态图(来源)图片地址
从图片可以看出线程通常有5种状态:
a. 新建状态(new):新创建一个线程对象;
b. 就绪状态(runnable):线程对象创建后,由其他线程调用其start()方法,该线程此时处于可运行线程池中,等待被调度,获取cpu使用权;
c. 运行状态(running):获取cpu时间片(timeslice),执行程序代码;
d. 阻塞状态(blocked):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态;
e. 死亡(dead):线程结束,run(),main()方法执行完毕,或者异常结束。
其中可以看到由三种阻塞:
(一). 等待阻塞:运行(running)的线程执行
o.wait()
方法,JVM会把该线程放入等待队列(waitting queue)中。
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁
时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:运行(running)的线程执行sleep(long ms)
或join()
方法,或者发出了I/O请求
时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
一篇不错的文章。
首先从创建状态入手,java创建Thread的几种方式:
First:直接继承Thread,重写run();方法:
public class Test{
public static void main(String[] args) {
MyThread myThread = new MyThread("Syong");
myThread.start();
//输出Hello World
}
}
class MyThread extends Thread{
public MyThread ( String name ) {
super(name);
}
@Override
public void run(){
System.out.println("Hello World");
}
}
Second:实现Runnable接口,重写run();方法:
public class Test{
public static void main(String[] args) {
MyThread myThread = new MyThread();
//创建一个线程
Thread runThread = new Thread(myThread);
//启动
runThread.start();
//输出Hello World
}
}
class MyThread implements Runnable{
@Override
public void run(){
System.out.println("Hello World");
}
}
Third:实现Callable,重写call();方法:
import java.util.concurrent.*;
public class Test{
public static void main(String[] args) {
MyThread myThread = new MyThread();
//用FutureTask接收返回值
FutureTask<String> futureTask = new FutureTask<String>(myThread);
//新建一个线程
Thread runThread = new Thread(futureTask);
runThread.start();
String result = null;
Integer count = 0;
try {
//阻塞当前线程(当前线程是main()),知道返回result
result = futureTask.get();
} catch (Exception e) {
e.getMessage();
}
System.out.println(result);
//结果:可能需要等待一会,就会输出Hello World;
}
}
class MyThread implements Callable<String>{
@Override
public String call() throws Exception{
return "Hello World";
}
}
以上就是java创建线程的几种方式,还有匿名方法/线程池方法,就不再提了,网上由很多例子,可以百度百度。
接下来,说说在java中Thread的sleep()/yield()/interrupt(),以及Object中的wait()/notify()/notifyAll()的区别:
Thread.sleep();:停止当前线程,进入阻塞状态,但并不释放资源(例如锁),但超时时,该线程进入可执行状态(就绪状态),等待系统分配时间片;
import java.util.concurrent.*;
public class Test{
public static Object obj = new Object();
public static void main(String[] args) throws Exception{
for ( int i = 0 ; i < 10 ; i ++) {
new Thread("Thread" + i){
@Override
public void run(){
synchronized(Test.obj) {
if ( Thread.currentThread().getName().equals("Thread6") ) {
try{
Thread.sleep(3000);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
System.out.println(Thread.currentThread().getName());
}
}
}.start();
}
//程序先输出一些ThreadName,停顿一下,继续输出一些ThreadName
}
}
Thread.yield();:停止当前线程,不会进入阻塞,直接进入可执行状态(就绪状态),有可能又立马执行,但是不会释放资源(例如锁);
t.interrupt();:中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行;
o.wait();:进入等待队列(进入这个状态会释放所占有的全部资源,与堵塞状态不同);
o.notify();/o.notifyAll();:进入wait这个状态后。是不能自己主动唤醒的,必须依靠其它线程调用notify()或notifyAll()方法才干被唤醒(因为notify()仅仅是唤醒一个线程,但我们由不能确定详细唤醒的是哪一个线程。或许我们须要唤醒的线程不可以被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池。等待获取锁标记。
死锁
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
以下例子是我通过理解上面概念写出来的一个死锁:
public class Test{
public static Object lockA = new Object();
public static Object lockB = new Object();
public static void main(String[] args) throws Exception{
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
}
}
class ThreadA extends Thread{
public static boolean use = false;
@Override
public void run(){
synchronized(Test.lockA) {
//使用自己的资源
ThreadA.use = true;
//模拟其他操作所浪费的时间
try{
Thread.sleep(1000);
}catch(Exception e ) {}
//监听资源
while ( ThreadB.use==true ) {
System.out.println(Thread.currentThread().getName() + " : get ResourseB false");
try{
Thread.sleep(1000);
}catch(Exception e ) {}
}
//获取
ThreadB.use = true;
System.out.println(Thread.currentThread().getName() + " : get resourceB");
//处理
try{
Thread.sleep(1000);
}catch(Exception e ) {}
//释放
ThreadA.use = false;
ThreadB.use = false;
System.out.println(Thread.currentThread().getName() + " : release resourceB");
}
}
}
class ThreadB extends Thread{
public static boolean use = false;
@Override
public void run(){
synchronized(Test.lockB) {
//使用自己的资源
ThreadB.use = true;
//模拟其他操作所浪费的时间
try{
Thread.sleep(1000);
}catch(Exception e ) {}
//监听资源
while ( ThreadA.use==true ) {
System.out.println(Thread.currentThread().getName() + " : get ResourseA false");
try{
Thread.sleep(1000);
}catch(Exception e ) {}
}
//获取
ThreadA.use = true;
System.out.println(Thread.currentThread().getName() + " : get resourceA");
//处理
try{
Thread.sleep(1000);
}catch(Exception e ) {}
//释放
ThreadA.use = false;
ThreadB.use = false;
System.out.println(Thread.currentThread().getName() + " : release resourceA");
}
}
}
Other
跟线程有关的东西有多线程,锁(同步锁synchronized/可重入锁ReentrentLock),JMM中的栈,Java中的Thread/ThreadLocal,线程之间的通信,volatile关键字等等,稍后介绍。
End
结束语:优秀是一种习惯。