有不对的地方,大家多多指教,闲话不多说请看下文
一、线程的概念
说到线程肯定会扯到进程,进程是什么?线程又是什么?大部分人都知道,进程通俗的来讲就是正在执行的应用程序,大家可以打开自己的任务管理器,可以看到好多正在执行的程序,这就是进程,而在进程中又包含很多线程,它是进程的组成部分
(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)
二、JAVA线程的分类
java中线程分为两种类型:用户线程和守护线程。 通过Thread.setDaemon(false)设置为用户线程; 通过Thread.setDaemon(true)设置为守护线程。如果不设置次属性,默认为用户线程。 守护线程为用户线程服务,当用户线程全部退出,则守护线程就停止了,GC就是守护线程
三、线程的创建方式
1、继承Thread类 代码一
public class CreateThread extends Thread{
@Override public void run() { for(int i=0;i<10;i++){
System.out.println(i);
}
}
public static void main(String[] args) {
CreateThread crThread=new CreateThread();
System.out.println("创建线程开始"); crThread.start();
System.out.println("创建线程结束"); }
}
2、实现Runable接口 代码二
public class CreateRunable implements Runnable{
@Override
public synchronized void run() {
for(int i=0;i<10;i++){
System.out.println(i);
}
}
public static void main(String[] args) {
CreateRunable crThread=new CreateRunable();
System.out.println("创建线程开始");
Thread read=new Thread(crThread);
Thread read1=new Thread(crThread);
read.run();
read1.run();
System.out.println("创建线程结束");
}
}
3、匿名内部类创建线程 代码三
public static void main(String[] args) {
//匿名内部类 Thread noName=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(i+"=");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
noName.run();
}
3、使用Callable和Future创建线程、
public class ThreadTest {
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
thread.start(); //线程进入到就绪状态
}
}
System.out.println("主线程for循环执行完毕..");
try {
int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable { private int i = 0;
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
4、使用线程池 后面会单独抽出一篇文章讲这个
四、线程的生命周期
线程声明周期一般分为以下五种
1、新建(new Thread) 当创建线程的一个实例,该线程处于新建状态
2、就绪(runable) 当线程被启动等待CPU分配时间片,也就是说线程正在就绪队列中等待获得CPU资源 例如集成Thread和实现runable接口的线程实例调用start()方法使线程处于就绪状态
3、运行(running) 线程获得CPU分配的资源后开始运行(执行run方法),此时线程除非自动放弃CPU资源或者有更高的优先级别进入,线程将运行结束
4、阻塞 由于某种原因导致正在运行的线程让出CPU资源停止自己的执行,即线程进入阻塞状态 一般遇到下面情况线程会进入阻塞状态 堵塞的情况分三种:
(一)等待堵塞:执行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)同步堵塞:执行的线程在获取对象的同步锁时,若该同步锁被别的线程占用。则JVM会把该线程放入锁池中。
(三)其它堵塞:执行的线程执行sleep()或join()方法,或者发出了I/O请求时。JVM会把该线程置为堵塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完成时。线程又一次转入就绪状态。
几个常用的阻塞方法解释
①线程调用sleep()方法使线程处于睡眠状态,睡眠时间到了会自动进入运行状态
②调用wait()方法使线程处于等待状态此时可以被notify()唤醒
③yield方法:放弃当前时间片,将Running状态转变为Runnable状态,不能指定多长时间。//假装忍让,完成具有不确定性不受监督的切换
④join方法:类似sleep,停止当前线程,让join线程先执行完毕,或执行指定的时间。//插队拼接
5、死亡 当线程执行完毕或者被其他线程杀死就会进入死亡状态,此时该线程不会在进入就绪队列进行等待执行 自然终止:正常运行run()方法后终止 异常终止:调用stop()方法让一个线程终止运行
五、线程生命周期转换图
1、整体图
2、详细的过程转换图
六、线程基础常问的面试题
- 进程与线程的区别? 答:进程是所有线程的集合,每一个线程是进程中的一条执行路径,线程只是一条执行路径。
- 为什么要用多线程? 答:提高程序效率
- 多线程创建方式? 答:继承Thread或Runnable 接口。
- 是继承Thread类好还是实现Runnable接口好? 答:Runnable接口好,因为实现了接口还可以继续继承。继承Thread类不能再继承。
- 你在哪里用到了多线程? 答:主要能体现到多线程提高程序效率。
- 举例:分批发送短信、迅雷多线程下载等。