基本概念
- 进程: 一个正在运行的程序, 其中至少包含一个线程.
- 线程: 独立的执行路径
- 在程序运行时, 即使自己没有创建线程,后台也会有多个线程, 如主线程, gc()线程
- main()称之为主线程, 即系统的入口, 用于执行整个程序.
- 在一个进程中,如果开辟了多个线程, 线程的运行是由调度器安排调度的, 调度器与操作系统紧密关联, 线程的运行顺序不能认为干预.
- 对于同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制.
- 多线程会带来额外的开销, 比如CPU的调度时间, 并发控制开销.
- 每个线程在自己的工作内存交互, 内存控制不当会造成数据不一致
多线程实现方式
- 方式一: 继承Thread类
package com.smile.test.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread extends Thread{
private String url;
private String fileName;
public TestThread(String url, String fileName){
this.url = url;
this.fileName = fileName;
}
@Override
public void run() {
Downloader downloader = new Downloader();
downloader.downloanImage(url,fileName);
}
class Downloader{
private void downloanImage(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url), new File(fileName));
System.out.println(fileName + " has already downloaded");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestThread t1 = new TestThread("http://www.4kbizhi.com/d/file/2020/06/05/small155838zgByl1591343918.jpg","one.jpg");
TestThread t2 = new TestThread("http://pic.netbian.com/uploads/allimg/190824/212516-1566653116f355.jpg","two.jpg");
TestThread t3 = new TestThread("http://pic.netbian.com/uploads/allimg/200604/001849-15912011292fcb.jpg","three.jpg");
t1.start();
t2.start();
t3.start();
}
}
输出:
two.jpg has already downloaded
one.jpg has already downloaded
three.jpg has already downloaded
Process finished with exit code 0
- 方式二: 实现Runnable接口
package com.smile.test.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread implements Runnable{
private String url;
private String fileName;
public TestThread(String url, String fileName){
this.url = url;
this.fileName = fileName;
}
@Override
public void run() {
Downloader downloader = new Downloader();
downloader.downloanImage(url,fileName);
}
class Downloader{
private void downloanImage(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url), new File(fileName));
System.out.println(fileName + " has already downloaded");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestThread t1 = new TestThread("http://www.4kbizhi.com/d/file/2020/06/05/small155838zgByl1591343918.jpg","one.jpg");
TestThread t2 = new TestThread("http://pic.netbian.com/uploads/allimg/190824/212516-1566653116f355.jpg","two.jpg");
TestThread t3 = new TestThread("http://pic.netbian.com/uploads/allimg/200604/001849-15912011292fcb.jpg","three.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
龟兔赛跑问题:
package com.smile.test.thread;
public class Race implements Runnable {
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 1000; i++) {
// 让兔子每50步睡1ms
if (Thread.currentThread().getName().equals("兔子") && i%50 == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断赛跑是否已经结束
if (gameOver(i)) {
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + '步');
}
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "兔子").start();
new Thread(race, "乌龟").start();
}
// 判断赛跑是否已经结束
private boolean gameOver(int step) {
if (winner != null){
return true;
} else if (step >= 1000){
winner = Thread.currentThread().getName();
System.out.println("比赛胜利者是" + winner);
return true;
} else {
return false;
}
}
}
- 方式三: 实现Callable接口
package com.smile.test.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
// 实现Callable接口,需要定义返回值类型.
public class TestCallable implements Callable<Boolean> {
private String url;
private String fileName;
// 构造方法
public TestCallable(String url, String fileName){
this.url = url;
this.fileName = fileName;
}
// 重写call()
@Override
public Boolean call() {
TestThread.Downloader downloader = new TestThread.Downloader();
downloader.downloadImage(url,fileName);
return true;
}
class Downloader{
private void downloadImage(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url), new File(fileName));
System.out.println(fileName + " has already downloaded");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("http://www.4kbizhi.com/d/file/2020/06/05/small155838zgByl1591343918.jpg","one.jpg");
TestCallable t2 = new TestCallable("http://pic.netbian.com/uploads/allimg/190824/212516-1566653116f355.jpg","two.jpg");
TestCallable t3 = new TestCallable("http://pic.netbian.com/uploads/allimg/200604/001849-15912011292fcb.jpg","three.jpg");
// 创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> submit1 = service.submit(t1);
Future<Boolean> submit2 = service.submit(t2);
Future<Boolean> submit3 = service.submit(t3);
//获取返回结果
System.out.println(submit1.get());
System.out.println(submit2.get());
System.out.println(submit3.get());
// 关闭执行服务
service.shutdownNow();
}
}
输出结果:
two.jpg has already downloaded
one.jpg has already downloaded
true
true
three.jpg has already downloaded
true
Process finished with exit code 0
好处:
- 能定义返回类型,获取返回值.
- 能抛出异常.