一、什么是多线程
1.线程与进程
进程:正在运行的程序。确切的来说,当一个程序进入内存运行,即是一个或多个进程在运行,具有一定独立功能。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
区别:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
2.多线程的好处
提高程序的运行效率,使CPU的使用率更高。但是多线程并不能提高运行速度。
二、多线程的两种实现方式
1.继承Thread类创建线程
#1.定义一个Thread类的子类
public class MyThread extends Thread{
//重写run方法,实现代码逻辑
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "线程!" + i);
}
}
}
#2.开启线程动作,查看线程的执行结果
public class Demo1 {
public static void main(String[] args) {
//创建线程对象
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
//开启线程动作
thread1.start();
thread2.start();
//返回main主线程
Thread mainTread = thread1.currentThread();
for (int i = 0; i < 10; i++) {
System.out.println(mainTread.getName()+ ":" + i);
}
}
}
2.实现Runnable接口创建线程
#1.定义线程执行目标子类
public class MyRunnable implements Runnable {
//定义线程执行目标的逻辑
@Override
public void run() {
//得到当前线程名字
Thread thread = Thread.currentThread();
for (int i = 0; i < 10; i++) {
System.out.println(thread.getName()+ ":" + i);
}
}
}
#2.开启线程动作,查看线程的执行结果
public class Demo2 {
public static void main(String[] args) {
//创建线程的执行目标对象
Runnable runnable = new MyRunnable();
//通过线程执行目标创建线程对象
Thread thread1 = new Thread(runnable);
thread1.setName("charliexm");
Thread thread2 = new Thread(runnable);
thread2.setName("charliexg");
//开启线程
thread1.start();
thread2.start();
Thread thread = thread1.currentThread();
for (int i = 0; i < 10; i++) {
System.out.println(thread.getName() + i);
}
}
}
3.两种实现方式的对比
继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。
实现Runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型,更加的符合面向对象。线程分为两部分,一部分线程对象,一部分线程任务。对线程对象和线程任务进行解耦。
4.使用匿名内部类创建线程示例
#方式一
//使用匿名内部类创建线程的子类对象
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("我的线程执行了");
}
};
thread.start();
//使用匿名内部类创建线程的子类匿名对象
new Thread() {
@Override
public void run() {
System.out.println("我的线程执行了2");
}
}.start();
#方式二:
//使用匿名内部类的方式,创建线程执行目标类对象
//创建线程执行目标类对象
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我的线程执行目标,执行了");
}
};
//通过目标创建线程对象
Thread thread2 = new Thread(runnable);
//开启线程
thread2.start();
#方式三
//使用匿名内部类的方式,创建线程执行目标类匿名对象
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我的线程执行目标,执行了2");
}
});
thread3.start();
//使用匿名内部类的方式,创建线程执行目标类匿名对象,并且创建的是线程的匿名对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我的线程执行目标,执行了3");
}
}).start();
三、线程池
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况
线程池的使用方法:
1.利用Runnable接口向线程池提交任务
#1.定义线程执行目标类
public class Ticket implements Runnable{
//定义线程执行目标的逻辑
@Override
public void run() {
// TODO Auto-generated method stub
}
}
#2.向线程池提交任务
public class ThreadPoolDemo1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建一个线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
//创建执行的任务类
Ticket ticket = new Ticket();
//提交任务
newFixedThreadPool.submit(ticket);
newFixedThreadPool.submit(ticket);
}
}
2.利用Callable接口向线程池提交任务
#1.定义线程执行目标
public class Ticket2 implements Callable<String>{
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "我是callable提交方式返回值";
}
}
#2.向线程池提交任务
public class ThreadPoolDemo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建一个线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
//callable的方式提交任务
Ticket2 ticket2 = new Ticket2();
Future<String> submit = newFixedThreadPool.submit(ticket2);
//从综合结果中得到返回值
String string = submit.get();
System.out.println(string);
}
}