目录
1.方式一:继承java.lang.Thread类(线程子类)
2.方式二:实现 java.lang.Runnable 接口(线程执行类)
3.实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常
一、线程的创建
● 通过创建Thread实例,完成线程的创建。
● 线程的内部实现可以通过继承Thread类、实现Runnable接口等方式进行封装。
● 通过调用Thread实例的start()方法启动新线程。
通过Thread类创建子线程,然后调用start()方法启动子线程,由操作系统调度,程序本身无法确定线程的调度顺序,子线程与主线程同时运行,每次执行结果不同。
注:直接调用Thread实例的run()方法是无效的,因为直接调用相当于调用了普通的Java方法,不会有任何改变,必须重写run()方法
//每一个线程都是一个Thread对象
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始执行......");
//创建子线程1
Thread thread1 = new Thread("线程1"){
@Override
public void run() {
for (int i = 0;i < 100;i ++){
System.out.println("子线程1:" + i);
}
}
};
//thread1.setPriority(10); //设置线程优先级(1-10),默认是5
thread1.start(); //启动线程1
//创建线程2
Thread thread2 = new Thread("线程2"){
@Override
public void run() {
for (char c = 'A'; c < 'z'; c++) {
System.out.println("子线程2:" + c);
}
}
};
//thread2.setPriority(1); //设置线程优先级(1-10),默认是5
thread2.start(); //启动线程2
System.out.println("主线程执行结束!!!!!");
}
}
二、线程的实现方式
1.方式一:继承java.lang.Thread类(线程子类)
通过Thread类创建子线程,重写run()方法,在方法体中实现子线程。代码如下:
//线程创建方式1:继承Thread类
public class Demo2 {
public static void main(String[] args) {
SubThread t1 = new SubThread("线程1:");
SubThread t2 = new SubThread("线程2:");
SubThread t3 = new SubThread("线程3:");
t1.start();
t2.start();
t3.start();
}
}
//继承Thread,重写run()方法
class SubThread extends Thread{
public SubThread(String name){
super(name);
}
@Override
public void run() {
for (char c = 'A';c <='Z';c ++){
System.out.printf("%s:%s\n",this.getName(),c);
}
}
}
2.方式二:实现 java.lang.Runnable 接口(线程执行类)
以邮件任务为例,编写子类实现Runnable接口,重写run()方法,完成子进程的实现。
//线程创建方式2:实现Runnable接口
public class Demo3 {
public static void main(String[] args) {
//每个Runnable接口的实现类,封装了线程执行逻辑
EmailTask emailTask = new EmailTask();
//创建3个线程,分别发送邮件
Thread t1 = new Thread(emailTask,"线程1:");
Thread t2 = new Thread(emailTask,"线程2:");
Thread t3 = new Thread(emailTask,"线程3:");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
//子类(邮件任务)
class EmailTask implements Runnable{
public void run(){
//获取当前线程对象
Thread currentThread = Thread.currentThread();
//获取线程名称
String name = currentThread.getName();
System.out.println(name + "使用JavaMail技术,通过smtp协议发送邮件");
}
}
3.实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常
创建任务类继承Callable()接口,重写call()方法,创建子线程,将子线程封装为FutureTask对象,然后启动。
//线程创建方式3:实现Callable接口
//用三个线程计算1-1000的累计和
public class Demo4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//Callable接口实现类:不同数据范围的计算任务
SunCalcTask task1 = new SunCalcTask(1,300);
SunCalcTask task2 = new SunCalcTask(301,600);
SunCalcTask task3 = new SunCalcTask(601,1000);
//Callable-->FutureTask(Runnable接口实现类)
FutureTask<Integer> fTask1 = new FutureTask<Integer>(task1);
FutureTask<Integer> fTask2 = new FutureTask<Integer>(task2);
FutureTask<Integer> fTask3 = new FutureTask<Integer>(task3);
//创建并启动线程
Thread thread1 = new Thread(fTask1);
Thread thread2 = new Thread(fTask2);
Thread thread3 = new Thread(fTask3);
//线程启动
//futureTask.run()-->callable.call()-->outcome结果
thread1.start();
thread2.start();
thread3.start();
//线程执行结束,分别湖区各自线程的返回结果
System.out.println("开始分别获取:");
Integer sum1 = fTask1.get();
Integer sum2 = fTask2.get();
Integer sum3 = fTask3.get();
//最终计算结果
Integer result = sum1 + sum2 + sum3;
System.out.println(result);
}
}
//通过Callable接口实现类SumCalcTask封装某个范围内的累加和
class SunCalcTask implements Callable<Integer>{
public SunCalcTask(Integer begin,Integer end){
this.begin = begin;
this.end = end;
}
private int begin,end;
@Override
public Integer call() throws Exception {
int total = 0;
for (int i = begin;i <= end;i++){
total += i;
}
System.out.println(Thread.currentThread().getName() + "线程执行了1次");
return total;
}
}
4.线程池
线程池,按照配置参数(核心线程数、最大线程数等)创建并管理若干线程对象。程序中如果需要使用线程,将一个执行任务传给线程池,线程池就会使用一个空闲状态的线程来执行这个任务。执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
//线程的创建方式4:通过线程池创建
public class Demo5 {
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 {
Thread.sleep(1000); //当前线程休眠1000毫秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
}
}
}
三、线程的休眠
可以通过调用Thread.sleep(long millis),强迫当前线程按照指定毫秒值休眠(休眠结束后自动唤醒),使用该方法可以人为控制某个线程优先执行。
Thread.sleep(1000) //控制线程休眠1000毫秒
四、线程的优先级
通过setPriority(int n)设置线程优先级,范围是1-10,默认为 5 ,优先级高的线程被操作系统调度的优先级较高(操作系统对高优先级线程,调度更频繁),但并不能保证优先级高的一定先执行。
thread1.setPriority(8); // 设置数字线程优先级=8
thread2.setPriority(1); // 设置字母线程优先级=1
小结
● Java用Thread对象表示一个线程,通过调用start()启动一个新线程;
● 一个线程对象只能调用一次start()方法;
● 线程的执行代码写在run()方法中;
● 线程调度由操作系统决定,程序本身无法决定调度顺序;
● Thread.sleep()可以把当前线程暂停一段时间,(时间结束后自动唤醒)