目录
一、什么是多线程
点击——》进程与线程的联系区别
二、多线程创建的几种方式
1、继承Thread
thread是程序执行的一个线程,Java虚拟机允许一个应用程序多线程同时执行,每个线程都有优先级,优先级高的比优先级低的线程优先执行,每个线程有可能会被标记为守护线程。
//继承Thread方式,重写run()方法 public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } public static void main( String[] args ) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start();//启动线程thread1 thread2.start();//启动线程thread2 } }
执行结果
Thread-1 is running
Thread-0 is running
2、实现Runnable接口
/** * 实现Runnable接口 */ public class MyThread1 implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } public static void main(String[] args) { MyThread1 myThread1 = new MyThread1(); Thread thread = new Thread(myThread1); thread.start(); } }
3、实现Callable接口
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; //实现Callable<V>接口,V为方法返回类型 public class CallableImpl implements Callable<Double> { @Override public Double call() throws Exception { Thread.sleep(1000); double result = Math.random(); System.out.println(Thread.currentThread().getName()+" is running !"); return result; } public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<Double> callable = new CallableImpl(); FutureTask<Double> future = new FutureTask<>(callable); //创建并启动线程 new Thread(future).start(); //调用get()会阻塞主线程,否则不会阻塞 Double result = future.get(); System.out.println("result=" + result); System.out.println("main is running !"); } }
4、线程池
public class ExecutorThread { public static void main(String[] args) { //使用 guava 开源框架的 ThreadFactoryBuilder 给线程池的线程设置名字 ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build(); ExecutorService service = new ThreadPoolExecutor(4,10,0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(1024), nameThreadFactory, new ThreadPoolExecutor.AbortPolicy()); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !")); service.shutdown(); } }
三、线程的生命周期
线程的五种基本状态:
1)新建状态(New):当线程对象创建后,线程进入新建状态,如:Thread t = new Thread();
2)就绪状态(Runnable):当调用线程对象的start()方法,即t.start(),线程就从新建状态变为就绪状态。处于就绪状态的线程只是说线程已经做好了准备,随时等待CPU调度执行,因此调用start()方法并不是说线程立即去执行,只是线程进入了一个可运行的状态。
3)运行状态(Running):当处于就绪状态的线程获得了CPU的调度执行后,此时线程进入运行状态。
4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
四、Thread中的一些方法
- currentThread():返回正在执行此方法代码的线程信息
- isAlive():判断当前的线程是否处于活动状态,所谓活动就是指线程已经启动但是尚未终止。线程处于正在运行或者准备开始运行的状态。
- sleep():让正在执行的线程暂停执行
- getId():返回当前线程的唯一标识
/*
* Thread ID
* 线程id,在调用init初始化方法的时候,调用nextThreadID()方法生成ID
*/
private long tid;
public long getId() {
return tid;
}
/**
*同步生成线程ID方法
*/
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
- getName():获得线程名称
线程名称类似“Thread-0”名称的由来:由线程的构造方法构造而来,如下图源码:
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
/* For autonumbering anonymous threads.
* 自动编号的匿名线程
*/
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
- yield():放弃当前的CPU资源,让给其他的线程去占用CPU执行时间。
因为放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
- isDaemon():判断线程是否是守护线程
五、如何停止一个线程
1、使用return停止线程:
public class Main extends Thread {
@Override
public void run() {
while (true){
if (this.isInterrupted()){
System.out.println("线程停止了!");
//使用return来停止线程
return;
}
}
}
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
main.start();
Thread.sleep(2000);
main.interrupt();
}
}
2、使用interrupt方法中断线程:
在Thread.java类里提供了两种方法判断线程是否为停止的。
this.interrupted():测试当前线程是否已经中断(静态方法)。如果连续调用该方法,则第二次调用将返回false。在api文档中说明interrupted()方法具有清除状态的功能。执行后具有将状态标识清除为false的功能。
this.isInterrupted():测试线程是否已经中断,但是不能清除状态标识。
3、抛异常法(推荐用法):
public class MyThread4 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 50000; i++) {
if (this.isInterrupted()) {
System.out.println( "线程已经结束,我要退出" );
// return;
throw new InterruptedException();
}
System.out.println( "i=" + (i + 1) );
}
System.out.println( "我是for下面的语句,我被执行说明线程没有真正结束" );
} catch (InterruptedException e) {
System.out.println( "进入MyThread.java类中run方法的catch异常了" );
e.printStackTrace();
}
}
}
4、在沉睡中停止:
@Override
public void run() {
super.run();
try {
System.out.println( "begin run" );
Thread.sleep( 500 );
System.out.println( "begin end" );
} catch (InterruptedException e) {
System.out.println("在沉睡中终止");
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
MyThread5 thread5 = new MyThread5();
thread5.start();
Thread.sleep( 20 );
thread5.interrupt();
} catch (InterruptedException e) {
System.out.println( "main catch" );
e.printStackTrace();
}
}
运行结果:
从打印结果看,sleep状态下停止某一个线程,会进入catch语句,并清除状态值,变成false
5、为什么不使用stop()方法停止线程
方法stop()已经弃用,因为如果强制让线程终止则有可能使一些请理性的工作得不到完成。另外,对锁的对象“解锁”,会导致数据得不到同步的处理,出现了数据不一致的情况。
六、暂停和恢复线程
suspend()——暂停线程
resume()——恢复线程
suspend()和resume()方法缺点——独占、不同步
七、线程的优先级
在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。
在Java中线程的优先级划分为1~10这10个等级。如果小于1或者大于10,则JDK抛出异常throw new IllegalArgumentException()。
JDK中使用了3个常量来预置定义优先级:
public final static int MIN_PRIORITY = 1;
public final static int MIN_PRIORITY = 5;
public final static int MIN_PRIORITY = 10;
线程优先级的特性:
1)线程的优先级具有继承特性,比如A线程启动B线程,则A线程与B线程的优先级是一样的
2)线程优先级具有规则性,CPU尽量将资源让给优先级比较高的线程
3)线程的优先级具有随机性,换句话说优先级较高的线程不一定每一次都先执行完
八、什么是守护线程?
在Java中线程分为两种:用户线程和守护线程
什么是守护线程?守护线程是一种特殊的线程。它的作用是为其他线程的运行提供便利服务,最典型的应用就是GC