一、线程:进程内部按顺序执行的任务,多个线程之间可以同时执行。线程共享进程的资源,但是线程之间不能共享资源,每个线程内部有自己的程序计数器。一个进程包含多个线程,至少存在一个主线程。
二、进程和线程的区别
1、进程是操作系统分配资源的最小单元;而线程是操作系统最小的执行单元。
2、进程中的线程共享其进程的资源和内存空间;而线程之间不能共享资源和内存空间。
3、进程可以独立执行;而线程必须依赖其进程才能执行。
4、一个进程中包含多个线程,这些线程之间可以同时进行执行。
5、进程资源开销大,而线程资源开销小。
三、线程的创建
总的来说,创建线程只有一种方法就是创建Thread对象,别的方法是从代码实现角度对其进行划分的,其内部代码实现终归还是在创建Thread对象。
1.创建一个Thread对象
//创建子线程1(继承Thread类)
System.out.println("主线程执行开始........");
//创建Thread对象,实现匿名类(该类不能被复用)
Thread t1 = new Thread("线程1") {
//重写run方法
@Override
public void run() {
for(int i=1;i<=100;i++) {
System.out.println("[线程1]:"+i);
}
}
};
//启动线程
t1.start();
//创建子线程2
//创建Thread对象,实现匿名类
Thread t2 = new Thread("线程2") {
@Override
public void run() {
for(char c='A';c<='x';c++) {
System.out.println("[线程2]:"+c);
}
}
};
//启动线程
t2.start();
System.out.println("主线程执行结束........");
2.创建线程的方式1:继承Thread类(不会返回执行结果)
public class Demo02 {
public static void main(String[] args) {
//创建SubThread对象,传入线程的名称
SubThread t1 = new SubThread("线程甲");
SubThread t2 = new SubThread("线程乙");
SubThread t3 = new SubThread("线程丙");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
//继承Tread类
class SubThread extends Thread{
public SubThread(String name) {
//调父类的构造方法,传入name
super(name);
}
//重写Tread类的run()方法
@Override
public void run() {
//循环输出大写字母
for(char c = 'A';c<='Z';c++) {
System.out.println(this.getName()+":"+c);
}
}
}
3、创建线程的方式2:实现Runnable接口(不会返回执行结果)
public class Demo03 {
public static void main(String[] args) {
//每个Runnable接口的实现类,封装了线程执行逻辑
EmailTask emailTask = new EmailTask();
//创建线程对象,传入"线程执行逻辑"和线程"名称",分别发送邮件
Thread t1 = new Thread(emailTask,"线程T1");
Thread t2 = new Thread(emailTask,"线程T2");
Thread t3 = new Thread(emailTask,"线程T3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
//子类(邮件任务)
//继承Task类,实现Runnable接口
class EmailTask extends Task implements Runnable{
//重写父类的方法(线程的执行)
@Override
public void execute() {
//获取当前线程对象
Thread thread = Thread.currentThread();
//获取线程名称
String threadName = thread.getName();
//模拟线程的执行逻辑
System.out.println(threadName + ":使用JavaMail技术,通过smtp协议发送邮件");
}
//因为Runnable接口实现了Thread类
@Override
public void run() {
execute();
}
}
//父类(任务)
abstract class Task{
public abstract void execute();
}
4、创建线程的方式3:实现Callable接口(泛型方法)
因为Thread对象的参数不接收Callable对象,所以要定义一个既可以接收Callable对象的,又可以被Thread对象接收的对象------->FutureTask对象。FutureTask调用时会调用run方法,而FutureTask类中又定义的有Callable对象,所以会调用call()方法。
public class Demo04 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//Callable接口的实现类:不同数据范围的计算任务
SumCalu sumCalu1 = new SumCalu(1,300);//1-300
SumCalu sumCalu2 = new SumCalu(301,500);//301-500
SumCalu sumCalu3 = new SumCalu(501,1000);//501-1000
//FutureTask类间接也是Callable的实现类
FutureTask<Integer> futureTask1 = new FutureTask<Integer>(sumCalu1);
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(sumCalu2);
FutureTask<Integer> futureTask3 = new FutureTask<Integer>(sumCalu3);
//创建线程对象,传入
Thread t1 = new Thread(futureTask1);
Thread t2 = new Thread(futureTask2);
Thread t3 = new Thread(futureTask3);
//启动线程
t1.start();
t2.start();
t3.start();
//线程执行结束,分别获取各自线程的返回结果
System.out.println("开始分别获取......");
Integer sum1 = futureTask1.get();
Integer sum2 = futureTask2.get();
Integer sum3 = futureTask3.get();
//汇总结果
System.out.println("汇总各自计算结果");
Integer sum = sum1+sum2+sum3;
System.out.println("汇总结果:"+sum);
}
}
//通过Callable接口实现类SumCalu封装某个范围内数据的累加和
class SumCalu implements Callable<Integer>{
private int begin,end;
//有参构造方法
public SumCalu(int begin,int end) {
this.begin = begin;
this.end = begin;
}
//执行逻辑
@Override
public Integer call() throws Exception {
int total = 0;
for(int i=begin;i<=end;i++) {
total += i;
}
//获取当前线程的名称
System.out.println(Thread.currentThread().getName());
return total;
}
}
5、创建线程的方式4:通过线程池创建
执行线程任务有两种方式:executer()和submit()。execute()只能提交Runnable类型的任务,且没有返回结果;submit()既能提交Runnable类型的任务,又能提交Callable类型的任务,且可以返回Future类型的结果。
public class Demo05 {
public static void main(String[] args) {
//创建固定数量(10)的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//不确定数量的线程池请求
while(true) {
//向线程池提交一个执行任务(Runnable接口实现类对象)
//线程池分配一个"空闲线程"执行该任务
//如果没有空闲线程,则该任务进入"等待队列(工作队列)"
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"进行了一次投票!");
try {
//当前线程休眠1000毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
四、线程生命周期的六种状态
1、NEW:新建状态,线程刚创建,还没有调用start()方法。
2、RUNNABLE:运行状态,调用了start()方法,线程即将执行或正在执行run()方法。
3、BLOCKED:阻塞状态,线程在竞争锁时,没有竞争到锁,被阻塞。
4、WATING:等待状态,调用sleep(),join()等方法。
5、Timed Wating:计时等待状态,调用sleep(毫秒值),join(毫秒值)等方法。
6、Terminated:终止状态,执行完run()方法。