一、创建线程的方式
有以下三种:
-
继承Thread类
-
实现Runnable接口 少用继承多用实现
-
实现Callable接口
1. 继承Thread类的方式创建新线程
-
创建自定义的类继承Thread类,并且重写方法;
-
实例化自定义类;
-
通过实例化对象调用start方法,来创建新线;
public class ExtendsThread extends Thread{
/**
* 重写run方法
*/
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " subThread start...");
System.out.println(Thread.currentThread().getName() + " subThread end");
}
public static void main(String[] args) {
ExtendsThread et = new ExtendsThread();
et.start();
}
}
2. 实现runnable接口
-
创建自定义类并实现Runnable接口,并实现接口中的run()方法;
-
实例化自定义的类;
-
将自定义类的实例作为参数给Thread类,创建thread实例;
-
调用thread实例的start方法,启动子线程;
public class TestRunnableThread extends Thread implements Runnable{
@Override
public void run() { //无返回值的public修饰的抽象方法
System.out.println(Thread.currentThread().getName() + "");
}
public static void main1(String[] args) {
TestRunnableThread rt = new TestRunnableThread();
Thread thread = new Thread(rt);
thread.start();
}
}
3. 实现Callable接口
callable接口的实现是线程池提供的一种创建线程的方式,具体的操作是:
-
实现Callable接口,并且实现call()方法
-
创建线程池(Executors工具类提供的方法创建线程池)
-
创建Callable接口实现类的实例
-
将实例对象通过线程池的submit方法提交给线程池进而创建新的线程
public class CallableThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "thread start");
Integer i = 100;
System.out.println(Thread.currentThread().getName() + "thread end");
return i;
}
public static void main1(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "runnable");
}
});
thread.start();
}
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor(); //创建线程池
CallableThread callableThread = new CallableThread(); //创建自定义类实现Callable接口,在对该类进行实例化
Future<Integer> future = executor.submit(callableThread); //将实例对象通过线程池的submit()提交给线程池,创建新的线程
try {
Integer integer = future.get();
future.cancel(true);
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二、线程操作的相关方法介绍
void start()
- 启动一个新的线程,start方法必须子线程第一个调用的方法,start不能够重复调用,新线程会调用runnable接口提供的run方法;
void run()
-
run方法是子线程的执行体,子线程从进入run方法开始直至run方法执行结束,意味着子线程的任务执行结束;
-
另外,在主线程中直接调用run方法不能创建新线程,只是普通方法调用;
yield()
: 是Thread类的静态方法
-
让步或者暂停,即让正在执行的线程停止或者让步,让给优先级较高的线程获取CPU的执行权;
-
但是不一定会真的让出CPU执行权,如等待的线程优先级较低或者当前只有一个线程在执行时,那么当先线程又会立即获取CPU的执行权;
sleep()
:是Thread类提供的静态方法
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos); //nanos指定时间的单位,默认为毫秒
- 让线程进行休眠,即阻塞线程,会抛出 InterruptedException异常;
join()
-
等待线程执行结束才继续执行,会抛出 InterruptedException异常
-
假如在a线程中b线程进行b.join调用,a线程等待b线程执行结束后才能继续执行,该方法用于控制多线程按照次序执行;
interrupt()
-
中断方法,底层调用native方法,native方法的作用是对特定标识位做修改;
-
主要作用于运行中线程和阻塞线程(sleep、join);
-
运行中的线程:interrupt方法仅仅是对标志位做了修改,其他没有影响;
-
阻塞线程:interrupt方法对标志位做了修改,令阻塞中的线程感知到标志位做了修改,就会中断当前的阻塞状态,抛出InterruptedException异常 ;
isInterrupted()
- 判断是否发生了中断操作,返回为Boolean类型;
- true:表示发生中断,false:表示未发生中断
setDaemon()
-
java中线程分为两种类型:用户线程和守护线程;
-
通过Thread.setDaemon(false)设置为用户线程;
通过Thread.setDaemon(true)设置为守护线程。如果不设置次属性,默认为用户线程;
isDaemon()
- 判断当前线程是否为守护线程,返回Boolean类型
- true:守护线程,false:非守护线程
setPriority(int newPriority)
-
设置线程优先级,优先级分为10级,优先级数字越大,即优先级越高;
-
优先级越高,则被优先调用的概率会大;
- MIN_PRIORITY = 1; 最小优先级
- NORM_PRIORITY = 5; 默认优先级
- MAX_PRIORITY = 10; 最大优先级
int getPriority()
- 返回当前程序的优先级
三、用户线程和守护线程
1.守护线程:是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,作为提供通用服务的线程存在;
- 守护线程–也称“服务线程”,在没有用户线程可服务时会自动离开。
2. 用户线程:
3. 用户线程和守护线程区别:
-
守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
-
生命周期:守护线程的生命周期是依赖于用户线程,当有用户线程存在,守护线程就会存活,当没有用户线程存在,那守护线程也随之消亡,垃圾回收是有单独线程来处理的,负责垃圾回收的线程就是守护线程;
-
主线程结束后用户线程还会继续运行,JVM存活;
-
如果没有用户线程,都是守护线程,那么在主线程结束之后守护线程和JVM也会结束;
-