目录
1.进程与线程
2.线程的实现方式
3.线程的生命周期
一、进程与线程
进程:运行的程序和它所需要的资源(CPU、内存)
线程:线程是进程的一部分,粒度比进程小,一个进程可以拥有多个线程,至少包含一个线程,这个线程就是主线程。
线程与进程的区别:
1.一个进程里面至少拥有一个线程
2.线程之间使用的资源是共享的。
3.线程之间的通信比进程间的通信开销小
4.线程的创建相对于进程的创建容易
ps:1.进程包含线程,可以有多个,至少有一个
2.线程是独立的,CPU抢占式,也就是当前运行的线程很有可能被暂停(挂起)很小时间片
3.进程间的内存空间是独立的,但是线程间的内存空间是共享的。
4.由于共享,线程间运行效率大大提高,利于线程间的通信(数据交换)
5.线程的创建比进程的创建小的多,使用比较多。
使用多线程的目的:
最大效率的使用CPU资源,提高程序的运行效率
ps:真正的多线程是建立在多核CPU的基础上的。
二、线程的实现方式
1.继承Thread 重写run方法
2.实现Runnable接口 重写run方法
3.带返回值的线程创建
Callable 和FutureTask
返回值获取方法 FutureTask对象的get()方法
ps:推荐使用第二种 。原因 :Java类的接口可以多实现,而继承只能单继承
注:run方法中如果有异常时不能抛出去的。 原因:run方法时重写的,而父类或端口中的run方法是没有申明异常的。
FutureTask的API
三、线程的生命周期
创建:实例化一个线程对象 new Thread()
就绪:start方法执行或者阻塞结束后
运行:获得CPU的时间片,执行run方法
阻塞:暂停(挂起),IO、sleep、wait、join
结束:run方法结束或者异常
线程的名称--主线程默认名称是main,子线程的默认名称则是Thread-0,Thread-1...
一下是线程的简单实现代码:
package com.demo.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 线程的简单实现
* @author Administrator
*
*/
/**
* 主线程
* @author Administrator
*
*/
public class ThreadExample {
/**
* 主线程里面启动子线程
* @param args
*/
public static void main(String[] args) {
//启动用继承实现的线程
ThreadExample1 threadExample1 = new ThreadExample1();
threadExample1.setName("线程1");
threadExample1.start();
//启动用接口实现的线程
new Thread(new ThreadExample2(),"线程2").start();
//启动带返回值的线程
FutureTask<String> futureTask = new FutureTask<>(new ThreadExample3());
try {
new Thread(futureTask,"线程3").start();
String s = futureTask.get();
System.out.println(Thread.currentThread().getName()+"--------"+s);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
/**
* 继承Thread的线程
* @author Administrator
*
*/
class ThreadExample1 extends Thread{
@Override
public void run() {
super.run();
for(int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--------"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 实现runnable接口的线程
* @author Administrator
*
*/
class ThreadExample2 implements Runnable {
@Override
public void run() {
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName()+"--------"+j);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 实现callable接口的带返回值的线程
* @author Administrator
*
*/
class ThreadExample3 implements Callable<String>{
@Override
public String call() throws Exception {
return "带返回值得线程";
}
}
线程的优先级
1-10 从低到高
默认的优先级是5,优先级越高,获得CPU的时间片越大
sleep方法
sleep方法能让线程进入阻塞状态,带一个时间参数(毫秒),经过休眠毫秒数后线程进入就绪状态。会有中断异常抛出
yield方法
yield方法能让线程直接进入就绪状态,会有中断异常抛出。
join方法
join方法能让线程合并,让多个线程合并到一个线程中进入阻塞状态,程序运行将会进入阻塞状态,会有中断异常抛出。
以下是join方法的具体代码与结果
package com.demo.thread; public class JoinDemo { public static void main(String[] args) { JoinThread thread = new JoinThread(); thread.start(); System.out.println("over"); } } class JoinThread extends Thread{ @Override public void run() { super.run(); for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"------"+i); } } }
package com.demo.thread; public class JoinDemo { public static void main(String[] args) throws InterruptedException { JoinThread t = new JoinThread(); t.start(); t.join(); System.out.println("over"); } } class JoinThread extends Thread{ @Override public void run() { super.run(); for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"------"+i); } } }
sleep方法与yield方法的区别
1)sleep方法带参数,可以指定休眠时长,进入阻塞状态CPU让给其他线程,当休眠时间到了后,进入就绪状态。
2)yield方法没有参数,CPU时间片让给同优先级或者优先级更高的线程,直接进入到就绪状态。有可能刚调用yield方法,又被CPU选中执行。
3)sleep方法抛出中断异常,yield方法没有
4)yied方法不太可控,所以一般不推荐使用来控制线程的并发问题sleep方法的可移植性强。