1.线程的概念:
什么是程序(
program
):为了完成某项特定的任务,使用某种语言,编写一组指令的集合。
即指
一段静态的代码
,静态对象。
什么是进程(
process
):
程序的一次执行过程,或是
正在运行的一个程序
。动态过程:有它自身的产生、存在和消亡的过程。
什么是线程(
thread
):在一个进程中,执行的一套功能流程,称为线程。
在一个进程中,执行的多套功能流程,称为多线程。
抢占式策略系统: 系统会分配给每个执行任务的线程一个很小的时间段用于执行任务,当该时间段用完后, 系统会剥夺其 cpu 使用权,交给其他线程执行。
2.为什么使用多线程?
为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰。
为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写文件,视频图像的采集,处理,显示,保存等。
3.多线程程序的优点:
提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
提高计算机系统CPU的利用率
改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
4.多线程的缺点:
如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.
更多的线程需要更多的内存空间
线程中止需要考虑对程序运行的影响.
通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生
5.多线程特点:
高内聚,低耦合
线程操作资源类
6.Java 实现多线程的几种方式
方式一:
① 声明一个类继承 Thread 类
② 重写 run() 方法,同时编写线程执行体
③ 创建该子类的实例
④ 调用 start() 方法启动线程,默认调用 run() 方法
public class MyThread extends Thread{
public MyThread(){}
public void run(){
for (int i = 0;i<100;i++) {
System.out.println("子線程"+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
}
}
方式二:
① 声明一个类实现 Runnable 接口
② 实现 run() 方法,同时编写线程执行体
③ 创建该实现类的实例
④ 创建 Thread 类的实例,将实现类的实例作为参数传递给 Thread 的构造器
⑤ 调用 Thread 的 start() 方法, 启动线程,默认调用 run() 方法
public class PrimeRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class TestThread2 {
public static void main(String[] args) {
PrimeRun pr = new PrimeRun();
Thread t1 = new Thread(pr);
t1.start();
Thread t2 = new Thread(pr);
t2.start();
}
}
说明:继承 Thread 类
与
实现 Runnable 接口的区别?
① Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
② 若需要多个线程访问共享数据时,首选使用 实现 Runnable 接口的方式,实现接口的方式解决了Java中单继承的局限性。
方式三:
package com.atguigu.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception{
System.out.println("--------Callable" );
return 200;
}
public static void main(String[] args) throws InterruptedException, ExecutionException{
@SuppressWarnings({ "rawtypes", "unchecked" })
FutureTask futureTask = new FutureTask( new MyThread());
new Thread(futureTask).start();
System.out.println("返回参数:"+futureTask.get());
}
}
说明:Runnable和Callable的区别?
① Callable需要实现call方法,而Runnable需要实现run方法;
② call方法还可以返回任何对象,无论是什么对象,JVM都会当作Object来处理,而Runnable没有返回结果。
③ Callable能够抛出checked exception,而Runnable不可以。
④ Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
⑤ Callable和Runnable都可以应用于executors。而Thread类只支持Runnable.
7.线程的生命周期:
(1) 线程运行状态说明:
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状 态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
(2) 线程错误的停止方法:在开发当中不可使用Stop()方法,已经过时,因为该方法具有不安全性;
线程正确的停止方法:使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。通过boolean循环控制程序的开始和结束
(3) 线程错误的中断方法:interrupt() 不能用于中断线程,在调用interrupt方法后, sleep方法抛出异常,然后输出错误信息:sleep interrupted.所以不使用
线程正确的中断方法 :使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。通过boolean循环控制程序的开始和结束
public class HelloThread1 implements Runnable{
int i = 0;
public static boolean flag = true;
public void setFlag(){
flag = false;
}
@Override
public void run() {
while(flag){
System.out.println(i++);
}
}
}
/*
* 结束线程 : 通常在线程执行体中写一些循环语句,因此控制住了循环,就相当于控制住了线程,即通知方式
*/
public class HelloThread1 {
public static void main(String[] args) throws Exception {
HelloThread1 ht1 = new HelloThread1();
Thread t1 = new Thread(ht1);
t1.start();
for (int i = 0; i < 100; i++) {
System.out.println("+++++"+i);
if(i==90){
ht1.setFlag();
System.out.println("控制从线程运行结束,此时主线程继续运行");
}
}
}
}