线程(Thread)
场景:



需要注意的是进程是静态的,进程中包括很多线程

main是主线程,
多核 CPU:现在很多 CPU 是多核的,比如双核、四核、八核等。每个核心都有自己独立的一套执行单元,所以在多核 CPU 情况下,不同核心可以在同一时间分别执行不同的线程,实现真正意义上的并行执行多个线程。

java中默认有main线程(用户线程),gc线程(守护线程)
三种创建方式:

创建线程的第一种方式:继承Thread类
调用.start()两条线程同时执行

交替执行:

如果调用run方法,那么肯定就是先走run方法

测试:


如果main()方法里调用重写run()方法,那么肯定就会按照代码顺序去执行

如果调用的是start()方法,那么就会同时进行
package com.itheima.mp;
//创建线程方式一,从写run()方法
public class TestThread extends Thread{
//从写run()方法
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("何家兴是我儿子"+i);
}
}
//主方法,main线程
public static void main(String[] args) {
TestThread t1 = new TestThread();
t1.run();//相当于就是调用run()方法,
for (int i = 0; i < 200; i++) {
System.out.println("何家兴是傻逼"+i);
}
t1.start();//相当于开启了一个线程,执行了run()方法
}
}
线程开启不一定立即执行,由cpu调度执行
注意注意注意:
1:首先让类继承Thread类
2:重写run()方法,
3:创建线程对象,一定要调用start()方法开启新的线程
下载器:
首先引入commnsio依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>

class WebDownloader{
public void download(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件下载失败,download方法出现问题,请稍后重试");
}
}
完整代码:
package com.itheima.mp;
import org.apache.commons.io.FileUtils;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.File;
import java.io.IOException;
import java.net.URL;
@SpringBootTest
public class TestThread2 extends Thread {
String url;
String name;
public TestThread2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
this.url=url;
this.name=name;
webDownLode webDownLode = new webDownLode();
webDownLode.webDownLode(url,name);//下载图片
System.out.println("文件下载完成,名字为"+name);
}
public static void main(String[] args) {
String url1="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url2="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url3="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String name1="地球1";
String name2="地球2";
String name3="地球3";
new TestThread2(url1,name1).start();
System.out.println("图片一开始下载");
new TestThread2(url2,name2).start();
System.out.println("图片二开始下载");
new TestThread2(url3,name3).start();
System.out.println("图片三开始下载");
}
//先写一个下载器
class webDownLode {
public void webDownLode(String url ,String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件下载出现异常,具体方法为:webDownLode()");
}
}
}
}
效果图:

我们发现,尽管方法的先后顺序是123,但是我们的线程完成顺序是不同的,体现了cpu来调度完成线程
注意:多线程继承Thread类重写run()方法时,只能重写一次
注意,通过继承Thread()类时,要启动run()方法用得是start()方法,例如:
new TestThread2(url1,name1).start();其中TestThread2是一个继承过Thread的类
构造函数:
用处一:初始化对象,方法名与类名相同,不能写返回值,void也不行,当使用 new 关键字创建类的一个新实例时,就会自动调用相应的构造函数,构造函数的主要作用是为对象的成员变量赋初始值。可以在构造函数内部通过参数接收外部传入的值,并将这些值赋给相应的成员变量,从而使对象在创建之初就具有特定的属性值
用处二:
除了初始化成员变量外,构造函数还可以执行一些与对象初始化相关的其他操作,比如初始化对象内部使用的一些资源(如打开文件、建立网络连接等,不过在实际应用中,这些操作通常需要在合适的时候进行关闭和释放,以避免资源浪费和潜在的问题),或者进行一些必要的校验工作(如检查传入的参数是否符合要求等
public class DatabaseConnection {
private Connection connection;
public DatabaseConnection() {
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3600/mydb", "user", "password");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
创建线程的第二种方式:实现Runnanle接口
步骤:
一:创建一个类实现Runnable接口
二:实现run()方法体,编写线程执行体
三:创建线程对象,调用start()方法启动多线程

还是拿这个下载图片举例子有两点不同,第一点,这个需要实现Runnable()接口,第二点,这个在创建多线程对象后,要再创建Thread()对象,然后调用Thread对象的start()方法来启动线程;
代码如下:
package com.itheima.mp;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThreadRunnable implements Runnable {
String name;
String url;
public TestThreadRunnable(String url, String name) {
this.name = name;
this.url = url;
}
@Override
public void run( ) {
WebImagedownlode imagedownlode = new WebImagedownlode();
System.out.println("图片开始下载,下载图片名称为"+name);
imagedownlode.imageDownLoad(url,name);
System.out.println("图片完成,下载图片名称为"+name);
}
public static void main(String[] args) {
String url1="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url2="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url3="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String name1="地球1";
String name2="地球2";
String name3="地球3";
TestThreadRunnable threadRunnable1 = new TestThreadRunnable(url1,name1);
TestThreadRunnable threadRunnable2 = new TestThreadRunnable(url2,name2);
TestThreadRunnable threadRunnable3 = new TestThreadRunnable(url3,name3);
new Thread(threadRunnable1).start();
System.out.println("图片一开始下载");
new Thread(threadRunnable2).start();
System.out.println("图片二开始下载");
new Thread(threadRunnable3).start();
System.out.println("图片三开始下载");
}
class WebImagedownlode{
public void imageDownLoad(String url ,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("WebImagedownlode方法出现异常,图片下载失败!");
}
}
}
}

推荐使用实现Runnable来实现多线程

强票问题:
package com.itheima.mp;
public class TestBuyTicket implements Runnable{
int tickeNumber=200;
public TestBuyTicket(){
this.tickeNumber=tickeNumber;
}
@Override
public void run() {
while (tickeNumber<0){
break;
}
while (tickeNumber>0) {
//模拟延时
try {
Thread.sleep(200);
{
System.out.println("线程"+Thread.currentThread().getName()+"拿到了第"+tickeNumber+"张票");
tickeNumber--;}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestBuyTicket buyTicket = new TestBuyTicket();
new Thread(buyTicket,"线程一").start();
new Thread(buyTicket,"线程二").start();
new Thread(buyTicket,"线程三").start();
}
}
我们首先定义了ticketNum=100,我们创建了多个线程,首先创建类对象,new TestBuyTicket ();
然后就是创建多个Thread()方法来创建多个线程对象
注意这里用到模拟延时:
Thread.sleep(200);这里的200指的是200毫秒;
第三种实现多线程的方法:实现Callable接口
首先说明Callable接口的好处:
一:可以定义返回类型
二:可以抛出异常
我们看看实现Callable接口的步骤:

我们用Call接口来实现图片下载案例:
package com.itheima.mp;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean> {
String name;
String url;
public TestCallable(String url, String name) {
this.name = name;
this.url = url;
}
@Override
public Boolean call( ) {
WebImagedownlode imagedownlode = new WebImagedownlode();
System.out.println("图片开始下载,下载图片名称为"+name);
imagedownlode.imageDownLoad(url,name);
System.out.println("图片完成,下载图片名称为"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
String url1="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url2="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url3="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String name1="地球1";
String name2="地球2";
String name3="地球3";
//
TestCallable t1 = new TestCallable(url1, name1);
TestCallable t2 = new TestCallable(url2, name2);
TestCallable t3 = new TestCallable(url3, name3);
//创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
Boolean b = r1.get();
Boolean b1 = r2.get();
Boolean b2 = r3.get();
//关闭服务
ser.shutdown();
}
class WebImagedownlode{
public void imageDownLoad(String url ,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("WebImagedownlode方法出现异常,图片下载失败!");
}
}
}
}
我们可以看到,这里是正常创建线程对象,
然后执行任务这里用了
ExecutorService ser = Executors.newFixedThreadPool(3);
然后就是需要提交任务,

这里可以获取结果,最后用 ser.shutdown()来结束服务
我们知道,用继承Thread抽象类是直接用 线程对象.start();
用Runnanle是创建线程对象后,再创还能Thread(线程对象)来启动线程
用Callable接口是用
//创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
Executors.newFixedThreadPool(3);这里是先用Excutors来newFixedThreadPool()来创建一个线程池,然后提交线程来执行
ser.submit():开启线程
sre.shutdown():结束线程
静态代理:
proxy:代理
package com.itheima.mp;
public class StaticProxy{
public static void main(String[] args) {
me me = new me();
WebCompany webCompany = new WebCompany(me);
webCompany.marry();
}
}
//结婚接口
interface Marry {
//久旱逢甘霖
//他乡遇故知
//同房花花烛夜
//金榜题名时
void marry();
}
class me implements Marry {
@Override
public void marry() {
System.out.println("付勇闯要结婚了");
}
}
class WebCompany implements Marry {
private Marry target;//目标结婚对象
public WebCompany(Marry target) {
this.target = target;
}
@Override
public void marry() {
before();
this.target .marry();
after();
}
//结婚前
private void before() {
System.out.println("我要结婚了");
}
private void after() {
System.out.println("我要生娃了!!!");
}
}
静态代理核心思想:
匿名内部类:匿名内部类是一种特殊的内部类,它没有显式的类名定义,是在创建对象的同时直接定义并实例化一个内部类,并且这个内部类通常是作为某个接口的实现类或者某个类的子类来使用的。
创建一个接口(接口内有个Marry()方法),然后写两个匿名内部类来实现这个接口,内部类Me()里面有个before()方法,然后内部类WebCompany里面有个结婚的方法,只不过这个方法需要一个Marry的实现类,我们把这个实现类me传进去,然后就是实现了代理的模式,通过婚庆公司来实现我结婚;
多线程五种状态:

详解:

线程方法:

一:线程停止

通过我们自己写一个标识位,让线程自己停止下来
1:在外部先立一个flag:
private flag=true;
在多线程方法体的时候通过
while(flag){
方法体内容
}
我们在main()函数外部写一个公开的标识位停止方法stop();
stop(){
this.flag=false;
}
我们在线程方法体中来调用这个方法去停止线程;
代码实现:
package com.itheima.mp;
//测试线程停止
//建议使用标志位---设置一个标志位
//不要使用stop 或者destory 等过时或者官方不建议的方法
public class TestThreadStop implements Runnable {
int n=100;
//设置一个标识位
private boolean flag=true;
//构造函数
TestThreadStop() {
this.n = n;
}
//切换标识位用的停止方法
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestThreadStop testThreadStop = new TestThreadStop();
new Thread(testThreadStop).start();
}
@Override
public void run() {
while (flag) {
System.out.println("我爱你!" + n + "次");
n--;
while (n==0){
TestThreadStop testThreadStop = new TestThreadStop();
//切换标识位,用我们自己停止线程,通过外部标识位
testThreadStop.stop();
}
}
}
}
线程休眠:

注意sleep()方法线程休眠单位是毫秒,如果要延时1秒。sleep(1000);
获取当前系统时间:Date time=new Date(System.currentTimeMillis());//获取系统当前时间
格式化时间:System.out.println(new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(time));
代码:
package com.itheima.mp;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep2 {
private boolean flag=true;
public void flag(){
this.flag=false;
}
public static void main(String[] args) throws InterruptedException {
new TestSleep2().tenDown();
}
public void tenDown() throws InterruptedException {
int n=20;
while (flag){
Date time=new Date(System.currentTimeMillis());//获取系统当前时间
System.out.println(n);
Thread.sleep(1000);
n--;
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time));
time=new Date(System.currentTimeMillis());//更新时间
if(n<=0){
flag();
}
}
}
}
以上是一个简单计时器
线程礼让( yield ):

代码:
package com.itheima.mp;
//线程礼让
public class TestYield implements Runnable{
//礼让不一定成功
public static void main(String[] args) {
TestYield testYield = new TestYield();
new Thread(testYield,"a").start();
new Thread(testYield,"b").start();
new Thread(testYield,"c").start();
}
@Override
public void run() {
new yeild().lirang();
}
}
class yeild{
public void lirang(){
System.out.println(Thread.currentThread().getName()+"准备就绪");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"已经结束");
}
}
线程强制执行:

用到的·方法是join()
插队插的是线程类
代码如下:
package com.itheima.mp;
public class JoinTest implements Runnable{
public static void main(String[] args) throws InterruptedException {
JoinTest joinTest = new JoinTest();
//启动我们的方法
Thread thread = new Thread(joinTest);
thread.start();
//主线程
for (int i = 0; i < 100; i++) {
System.out.println("main"+i);
if (i==50){
thread.join();//插队
}
System.out.println("main"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程vip来了"+i);
}
}
}
线程状态检测:

死亡之后的线程不能再次启动,调用start的时候不一定立即执行,只有等cpu调度到它的时候才会执行
package com.itheima.mp;
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
System.out.println(i);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread.State state = thread.getState();
//观测状态
System.out.println(state);
//线程启动
thread.start();
System.out.println(state);
//现场结束
while (state != Thread.State.TERMINATED) {
Thread.sleep(100);
Thread.State state1 = thread.getState();
//只要线程不终止,就一直输出状态
System.out.println(state1);
}
}
}
线程优先级:
线程优先级的话
优先级范围及默认值
Java 中的线程优先级取值范围是在 1(MIN_PRIORITY)到 10(MAX_PRIORITY)之间,其中 5(NORM_PRIORITY)是默认的线程优先级。例如,主线程默认的优先级就是 NORM_PRIORITY。
可以通过setPriority方法来设置线程的优先级。以下是一个简单示例:
public class ThreadPriorityExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
// 线程要执行的任务内容
for (int i = 0; i < 1000; i++) {
System.out.println("线程1执行:" + i);
}
});
Thread thread2 = new Thread(() -> {
// 线程要执行的任务内容
for (int i = 0; i < 1000; i++) {
System.out.println("线程2执行:" + i);
}
});
// 设置线程1的优先级为7
thread1.setPriority(7);
// 线程2使用默认优先级5
thread2.start();
thread1.start();
}
}
线程同步:
用关键字synchornized
我们知道ArrayList集合是不安全的,当我们创建一万个线程,然后把这10000个线程的名字放入到集合中,最后我们打印出集合的size,我们发现集合的size不为一万,我们通过synchronized(){
}来实现同步
代码:
package com.itheima.mp;
import java.util.ArrayList;
//测试ArrayList集合
public class TestList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add( (Thread.currentThread().getName()));
}
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
使用synchronized关键字对整个list对象进行同步虽然能保证数据的一致性,但在高并发场景下可能会导致性能下降。因为同一时刻只有一个线程能够进入同步块对list进行操作,其他线程需要等待,这会造成大量的线程等待时间,降低了整体的执行效率。
如果不加synchronized

可以看到非常不安全
测试JUC安全类型的集合:
copyOnWriteArrayList这个集合是安全的,也就是人家给我们实现好的,可以直接拿来用,但是使用这种集合可能会带来效率问题,代码实现:
package com.itheima.mp;
import java.util.concurrent.CopyOnWriteArrayList;
public class JUCtest {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}

死锁:

package com.itheima.mp;
//死锁:多个线程互相抱着对方的资源,然后形成僵持
public class DeadLOck {
public static void main(String[] args) {
MakeUp girl1=new MakeUp(0,"灰姑娘");
MakeUp girl2=new MakeUp(1,"白雪公主");
girl1.start();
girl2.start();
}
}
//口红
class Lipstick{
}
class Mirror{
}
class MakeUp extends Thread{
//需要的资源只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
MakeUp(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//互相持有对方的资源
private void makeup() throws InterruptedException {
if(choice==0){
synchronized(lipstick){
//获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
synchronized(mirror){
//一秒钟后获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
}
}
}else {
synchronized(mirror){
//获得口红的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
synchronized(lipstick){
//一秒钟后获得镜子的锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
效果图:程序会一直运行

就形成了死循环
解决办法:
package com.itheima.mp;
//死锁:多个线程互相抱着对方的资源,然后形成僵持
public class DeadLOck {
public static void main(String[] args) {
MakeUp girl1=new MakeUp(0,"灰姑娘");
MakeUp girl2=new MakeUp(1,"白雪公主");
girl1.start();
girl2.start();
}
}
//口红
class Lipstick{
}
class Mirror{
}
class MakeUp extends Thread{
//需要的资源只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
MakeUp(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//互相持有对方的资源
private void makeup() throws InterruptedException {
if(choice==0){
synchronized(lipstick){
//获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized(mirror) {
//一秒钟后获得镜子的锁
System.out.println(this.girlName + "获得镜子的锁");
}
}else {
synchronized(mirror){
//获得口红的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized(lipstick){
//一秒钟后获得镜子的锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
把锁拿出来,这就是啥,占据口红想得到镜子,另一个占据镜子想要口红,最后僵持不下,形成死锁,解决办法,锁里面不要套嵌锁

Lock锁:

package com.itheima.mp;
import java.util.concurrent.locks.ReentrantLock;
//测试LOCK锁
public class TestLocks {
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
}
class TestLock implements Runnable {
int ticketNum = 10;
//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
if (ticketNum > 0) {
System.out.println(ticketNum--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
break;
}
}finally {
lock.unlock();
}
}
}
}
加锁和解锁:
首先我们看到一个方法:
private final ReentrantLock lock = new ReentrantLock();
lock.lock();//加锁
这样我们就生成了一个锁,最后我们要解锁
lock.unlock();
我们上次用到的CopyOnWriteArrayList<>();
我们注意到:Lock锁需要我们手动去解锁,而synchronized()是隐式锁,他会自动解锁
我们记得使用顺序:
Lock锁>同步代码块(已经进入了方法体,分配了相应的资源)>同步方法(在方法体之外)
管程法:
线程池:

使用线程池:

我们依稀记得在创建线程的时候我们使用的第三种方法实现impliement callable接口的时候,
代码:
package com.itheima.mp;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean> {
String name;
String url;
public TestCallable(String url, String name) {
this.name = name;
this.url = url;
}
@Override
public Boolean call( ) {
WebImagedownlode imagedownlode = new WebImagedownlode();
System.out.println("图片开始下载,下载图片名称为"+name);
imagedownlode.imageDownLoad(url,name);
System.out.println("图片完成,下载图片名称为"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
String url1="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url2="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String url3="https://tse4-mm.cn.bing.net/th/id/OIP-C.jdP04yEoxG10mcywseQj7gAAAA?rs=1&pid=ImgDetMain";
String name1="地球1";
String name2="地球2";
String name3="地球3";
//
TestCallable t1 = new TestCallable(url1, name1);
TestCallable t2 = new TestCallable(url2, name2);
TestCallable t3 = new TestCallable(url3, name3);
//创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
Boolean b = r1.get();
Boolean b1 = r2.get();
Boolean b2 = r3.get();
//关闭服务
ser.shutdown();
}
class WebImagedownlode{
public void imageDownLoad(String url ,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("WebImagedownlode方法出现异常,图片下载失败!");
}
}
}
}
这里我们看到他是用Executors来创建线程池,
TestCallable t1 = new TestCallable(url1, name1);
TestCallable t2 = new TestCallable(url2, name2);
TestCallable t3 = new TestCallable(url3, name3);
//创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
Boolean b = r1.get();
Boolean b1 = r2.get();
Boolean b2 = r3.get();
//关闭服务
ser.shutdown();
先创建线程,然后就是创建一个线程池,把要创建的线程数目放进去,然后通过返回的ExecutorService 来通过submit来开启线程,获取结果
最后关闭服务
这个是利用Callable来创建线程,Callabl用的是submit,而Runnable来启动线程可以用thread.start或者用servive.execute()
上代码:
package com.itheima.mp;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool
{
public static void main(String[] args) {
//创建了一个线程池,里面有三个线程
ExecutorService service = Executors.newFixedThreadPool(3);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
总结:
代码:
package com.itheima.mp;
import java.util.concurrent.*;
public class ReviewThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//
He he = new He();
he.start();
//通过Runnable
me1 me1 = new me1();
new Thread(me1).start();
//通过Callable
GMX gmx1 = new GMX();
System.out.println(gmx1.toString());
GMX gmx2 = new GMX();
GMX gmx3 = new GMX();
ExecutorService service = Executors.newFixedThreadPool(10);
Future<String> submit = service.submit(gmx1);
String s = submit.get();
System.out.println(gmx1.toString());//输出的应该是hash值
Future<String> submit1 = service.submit(gmx2);
String s1 = submit.get();
System.out.println(gmx2.toString()+s1);
Future<String> submit2 = service.submit(gmx3);
String s2 = submit2.get();
System.out.println(gmx3.toString()+s2);
service.shutdown();
ExecutorService service2 = Executors.newFixedThreadPool(10);
service2.submit(me1);
service2.shutdown();
}
}
//通过Thread来创建线程
class He extends Thread{
@Override
public void run() {
System.out.println("大家好,我是何佳兴,用的是Thread");
}
}
//通过Runnable来创建线程
class me1 implements Runnable{
@Override
public void run() {
System.out.println("大家好,我是付永闯,用的是Runnable");
}
}
class GMX implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("大家好,我是郭明轩,用的是Callable");
return "大家好,我是郭明轩,用的是Callable";
}
}
附图:
