一、普通方法调用和多线程
二、进程(Process)和线程(Thread)。
1、提起进程,就不得不说程序。程序是指令和数据的有序集合,其本身没有任何运行意义,是一个静态概念。
2、进程则是执行程序的一次执行过程,是一个动态概念。是系统资源分配的单位。
3、通常在一个进程中包含多个线程,当然一个进程至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cup,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,故有同时执行的错觉。
三、三种创建方式:
1、Thread class——继承Thread类(不推荐使用)
①自定义线程类继承Thread类
②重写run()方法,编写线程执行体
③创建线程对象,调用start()方法启动线程。
代码:
package java_01.com;
/**
* 多线程
* */
//创建线程的方式一:继承Thread类,重写run()方法,调用start()开启线程。
public class TestThread extends Thread {
@Override
public void run(){
//run方法线程体
for(int i=0;i<200;i++){
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
//main()线程,主线程
//创建一个线程对象
TestThread testThread = new TestThread();
//调用start()开启线程
testThread.start();
for(int i=0;i<200;i++){
System.out.println("我在打游戏"+i);
}
}
}
运行:
例:使用Thread,多线程同步下载图片
在src下创建lib包下载并导入commons-io-2.6.jar包
代码:
package java_01.com;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/*
练习Thread,实现多线程同步下载图片*
*/
public class TestThread1 extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名称
public TestThread1(String url,String name){
this.url=url;
this.name=name;
}
//下载图片线程的执行体
@Override
public void run(){
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
TestThread1 t1 = new TestThread1("https://pics7.baidu.com/feed/aec379310a55b319177d007a03928a2ccefc17fa.jpeg?token=718d823c64f6ce30de333d6f9d37da59","1.jpg");
TestThread1 t2 = new TestThread1("https://pics2.baidu.com/feed/4bed2e738bd4b31ce423c63ac7ed2f759f2ff8dd.jpeg?token=cb277435332c155244f9330512dc33e4","2.jpg");
TestThread1 t3 = new TestThread1("https://pics2.baidu.com/feed/9d82d158ccbf6c810130f973fd05b93f32fa40ef.jpeg?token=498f20a4caa55065f9ca2fdaf709c49c","3.jpg");
//先下载t1
t1.start();
//然后下载t2
t2.start();
//最后下载t3
t3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try{
FileUtils.copyURLToFile(new URL(url), new File(name));
}catch(IOException e){
e.printStackTrace();
System.out.println("IO异常,downloader方法出现的问题");
}
}
}
运行:
2、Runnable接口——实现Runnable接口(推荐使用)
①实现Runnable接口。
②重写run方法。
③执行线程需要丢入Runnable实现类,调用start()方法。
代码:
package java_01.com;
//创建线程方式2:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start()方法。
public class TestThread2 implements Runnable{
@Override
public void run(){
//run方法线程体
for(int i=0;i<200;i++){
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
TestThread2 testThread2 = new TestThread2();
//创建线程对象,通过线程对象来开启我们的线程,代理
// Thread thread = new Thread(testThread2);
// thread.start();
new Thread(testThread2).start();
for(int i=0;i<200;i++){
System.out.println("我在打游戏"+i);
}
}
}
运行:
实现线程方法1和2对比:
②不同点:方法1继承了Thread类,直接创建了一个Thread对象来调用start方法,方法2则需要一个代理,通过一个Thread把Runnable接口的实现类丢进去调用start,本质上都相同。在方法1中Thread也是实现了Runnable接口。
①共同点:都重写了run方法
例:
代码:
package java_01.com;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/*
练习Thread,实现多线程同步下载图片*
*/
public class TestThread1 implements Runnable{
private String url;//网络图片地址
private String name;//保存的文件名称
public TestThread1(String url,String name){
this.url=url;
this.name=name;
}
//下载图片线程的执行体
@Override
public void run(){
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
TestThread1 t1 = new TestThread1("https://pics7.baidu.com/feed/aec379310a55b319177d007a03928a2ccefc17fa.jpeg?token=718d823c64f6ce30de333d6f9d37da59","1.jpg");
TestThread1 t2 = new TestThread1("https://pics2.baidu.com/feed/4bed2e738bd4b31ce423c63ac7ed2f759f2ff8dd.jpeg?token=cb277435332c155244f9330512dc33e4","2.jpg");
TestThread1 t3 = new TestThread1("https://pics2.baidu.com/feed/9d82d158ccbf6c810130f973fd05b93f32fa40ef.jpeg?token=498f20a4caa55065f9ca2fdaf709c49c","3.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name){
try{
FileUtils.copyURLToFile(new URL(url), new File(name));
}catch(IOException e){
e.printStackTrace();
System.out.println("IO异常,downloader方法出现的问题");
}
}
}
运行:
小结前两种创建线程的方式:
①继承Thread类:
a.子类继承Thread类具备多线程能力。
b.启动线程:子类对象.start。
c.不建议使用:避免OOP单继承局限性。
②实现Runnable接口
a.实现接口Runnable具有多线程能力。
b.启动线程:传入目标对象+Thread对象.start()。
c.推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
3、Callable接口——实现Callable接口
①Callable接口的特点:类似于Runnable接口,但是Runnable没有返回值,并且无法抛出经过检查的异常,而Callable依赖FutureTask类获取返回结果。
没有线程池:
代码演示:
public class Callabledemo{
public static void main(String[] args) throws Exception{
MyThread = new MyThread();
FutureTask<Integer> result = new FutureTask<Integer>(mt);
new Thread(result).start();
// 获取运算结果是同步过程,即call方法执行完成,才能获取结果
Integer sum = result.get();
System.out.println(sum);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
}
单个线程池:用ExecutorService、Callable、Future实现有返回值结果的线程
ExecutorService提供了submit()方法,传递了一个Callable或Runnable,返回Future,如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
public class MyCallable implements Callable<String> {
/**
* 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
*/
@Override
public String call() {
System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
return "result";
}
public static void main(String[] args) {
test1();
}
/**
* 单个线程
*/
public static void test1() {
// 1.创建固定大小的线程池
ExecutorService es = Executors.newFixedThreadPool(1);
// 2.提交线程任务,用Future接口接受返回的实现类
Future<String> future = es.submit(new MyCallable());
// 3.关闭线程池
es.shutdown();
// 4.调用future.get()获取callable执行完成的返回结果
String result;
try {
result = future.get();
System.out.println(Thread.currentThread().getName() + "\t" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
结果:
pool-1-thread-1 执行callable的call方法
main result
多个线程的执行返回结果:
public class MyCallable implements Callable<String> {
/**
* 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
*/
@Override
public String call() {
System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
return "result";
}
public static void main(String[] args) {
test2();
}
/**
* 多个线程
*/
public static void test2() {
// 1.创建固定大小的线程池(5个)
int threadNum = 5;
ExecutorService es = Executors.newFixedThreadPool(threadNum);
// 2.提交线程任务,用Future接口接受返回的实现类
List<Future<String>> futures = new ArrayList<Future<String>>(threadNum);
for (int i = 0; i < threadNum; i++) {
Future<String> future = es.submit(new MyCallable());
futures.add(future);
}
// 3.关闭线程池
es.shutdown();
// 4.调用future.get()获取callable执行完成的返回结果
for (Future<String> future : futures) {
try {
String result = future.get();
System.out.println(Thread.currentThread().getName() + "\t" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
结果:
pool-1-thread-1 执行callable的call方法
pool-1-thread-2 执行callable的call方法
pool-1-thread-4 执行callable的call方法
pool-1-thread-3 执行callable的call方法
main result
pool-1-thread-5 执行callable的call方法
main result
main result
main result
main result