首页 > 其他分享 >面试八--多线程(一)线程创建的四种方法

面试八--多线程(一)线程创建的四种方法

时间:2023-02-06 13:37:04浏览次数:44  
标签:Thread -- 创建 MyCallable 线程 new 多线程 public


1 进程和线程的概念

进程是程序的运行实例,线程是进程中独立执行的最小单位

2 线程的创建、启动与应用

在Java平台中创建一个线程就是创建一个Thread类的实例。线程的任务处理可以在Thread类中的run方法直接实现或者通过该方法进行调用。

Thread类有两种常用的构造器:Thread()和Thread(Runnable target)。相应的java 语言创建线程有两种方式。创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法(继承)。另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程(实现接口)。

2.1 创建线程方式一继承Thread类

1 定义一个类继承Thread。

2 重写run方法。

3 创建子类对象,就是创建线程对象。

4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

public class Demo01 {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread("新的线程!");
//开启新线程
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 10; i++) {
System.out.println("main线程!"+i);
}
}
}
public class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
/**
* 重写run方法,完成该线程执行的逻辑
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在执行!"+i);
}
}
}

2.2 创建线程方式二实现Runnable接口

1、定义类实现Runnable接口。

2、覆盖接口中的run方法。。

3、创建Thread类的对象

4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。

5、调用Thread类的start方法开启线程。

public class Demo02 {
public static void main(String[] args) {
//创建线程执行目标类对象
Runnable runn = new MyRunnable();
//将Runnable接口的子类对象作为参数传递给Thread类的构造函数
Thread thread = new Thread(runn);
Thread thread2 = new Thread(runn);
//开启线程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程:正在执行!"+i);
}
}
}
public class MyRunnable implements Runnable{

//定义线程要执行的run方法逻辑
@Override
public void run() {

for (int i = 0; i < 10; i++) {
System.out.println("我的线程:正在执行!"+i);
}
}
}

2.3 两种创建方式的区别,哪种方式更好一些?

Runnalble方式更好一些,因为

(1)实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。实现Runnable接口的方式是低耦合的。(2)java不支持多继承,但是支持多实现,因此要想有多个父类,Runnalbe更合适

2.4 创建线程方式三  通过Callable和FutureTask创建线程

      Callable接口:与Runnable接口功能相似,唯一的区别是它能够返回值,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
     a:创建Callable接口的实现类 ,并实现Call方法
     b:创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
     c:使用FutureTask对象作为Thread对象的target创建并启动线程
     d:调用FutureTask对象的get()来获取子线程执行结束的返回值
通过线程池中的线程对象,使用Callable接口完成两个数求和操作:

class MyCallable implements Callable<Integer> {
//成员变量
int x = 5;
int y = 3;
//构造方法
public MyCallable(){
}
public MyCallable(int x, int y){
this.x = x;
this.y = y;
}

@Override
public Integer call() throws Exception {
return x+y;
}
}

public class ThreadPoolDemo {
public static void main(String[] args) {
MyCallable c = new MyCallable(100, 200);
MyCallable c2 = new MyCallable(10, 20);
FutureTask<Integer> f1 = new FutureTask<Integer>(c);
FutureTask<Integer> f2 = new FutureTask<Integer>(c2);
Thread t=new Thread(f1);
Thread t2=new Thread(f2);
t.start();
t2.start();
Integer sum,sum2;
try {
sum = f1.get();
System.out.println("sum=" + sum);
sum2= f2.get();
System.out.println("sum2=" + sum2);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

}

    2.5 创建线程方式三  通过线程池创建线程

通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。        

面试八--多线程(一)线程创建的四种方法_System

我们使用线程池创建2个线程:

  1. Executors:线程池创建工厂类
  2. public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
  3. ExecutorService:线程池类

public class ThreadPoolTest {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
//创建Runnable实例对象
MyRunnable r = new MyRunnable();

service.execute(r);
service.execute(r);

}
}class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");

try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " +Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}

 

面试八--多线程(一)线程创建的四种方法_System_02

求两个数的之和:

public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(2);

//创建一个Callable接口子类对象
//MyCallable c = new MyCallable();
MyCallable c = new MyCallable(100, 200);
MyCallable c2 = new MyCallable(10, 20);

//获取线程池中的线程,调用Callable接口子类对象中的call()方法, 完成求和操作
//<Integer> Future<Integer> submit(Callable<Integer> task)
// Future 结果对象
Future<Integer> result = threadPool.submit(c);
//此 Future 的 get 方法所返回的结果类型
Integer sum = result.get();
System.out.println("sum=" + sum);

//再演示
result = threadPool.submit(c2);
sum = result.get();
System.out.println("sum=" + sum);
//关闭线程池(可以不关闭)

}
}
public class MyCallable implements Callable<Integer> {
//成员变量
int x = 5;
int y = 3;
//构造方法
public MyCallable(){
}
public MyCallable(int x, int y){
this.x = x;
this.y = y;
}

@Override
public Integer call() throws Exception {
return x+y;
}
}

3 Thread的常用方法

面试八--多线程(一)线程创建的四种方法_java_03

面试八--多线程(一)线程创建的四种方法_线程池_04

1.线程休眠sleep();:线程有优先级,但是我们可以用此方法人为的改变它们的优先级,让线程暂停,它其他线程获得分配空间。
用法:Thread.sleep(2000);//休眠两秒
2.线程让步yield();就是让出自己的分配空间给其他线程,那么问题来了,让步多久呢?如果有两条线程的话,是不是让步到另外一条线程执行完呢?经测试,不是让另外的线程执行,让步时间是不确定的;
注意:当某个线程调用yield()方法之后,只有与当前线程优先级形同或者更高的才能获得执行机会。 (直接进入可运行状态,有可能调用完自己又被CPU调度到,又直接变成运行状态了。)
用法:一般都是指定条件,如if(i==10){Thread.yield();}
3.线程插队join():当某个程序调用其他线程的join()时,调用线程将会阻塞,直到插入的线程运行完毕,才运行该线程,如main线程中for(int i = 0; i < 100; i++){if(i == 2){t.join();}},当i等于2时,线程t将执行完毕再执行main中余下的 i= 3i=4 ……
用法:一般都是指定条件,如if(i==10){t.join();}注意插队肯定是在别的线程中插别人的队,不可能在自己的线程中写join(); 如:t线程中写t.join();,这种方法是不正确的。

4 实践:开启5个线程从网站上下载文件保存到本地

package createThread;

import io.github.viscent.mtia.util.Debug;
import io.github.viscent.mtia.util.Tools;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

public class FileDownloaderApp {

public static void main(String[] args) {
Thread downloaderThread = null;
for (String url : args) {
// 创建文件下载器线程
downloaderThread = new Thread(new FileDownloader(url));
// 启动文件下载器线程
downloaderThread.start();
}
}

// 文件下载器
static class FileDownloader implements Runnable {
private final String fileURL;

public FileDownloader(String fileURL) {
this.fileURL = fileURL;
}

@Override
public void run() {
Debug.info("Downloading from " + fileURL);
String fileBaseName = fileURL.substring(fileURL.lastIndexOf('/') + 1);
try {
URL url = new URL(fileURL);
String localFileName = System.getProperty("java.io.tmpdir")
+ "/viscent-"
+ fileBaseName;
Debug.info("Saving to: " + localFileName);
downloadFile(url, new FileOutputStream(
localFileName), 1024);
} catch (Exception e) {
e.printStackTrace();
}
Debug.info("Done downloading from " + fileURL);
}

// 从指定的URL下载文件,并将其保存到指定的输出流中
private void downloadFile(URL url, OutputStream outputStream, int bufSize)
throws MalformedURLException, IOException {
// 建立HTTP连接
final HttpURLConnection httpConn = (HttpURLConnection) url
.openConnection();
httpConn.setRequestMethod("GET");
ReadableByteChannel inChannel = null;
WritableByteChannel outChannel = null;
try {
// 获取HTTP响应码
int responseCode = httpConn.getResponseCode();
// HTTP响应非正常:响应码不为2开头
if (2 != responseCode / 100) {
throw new IOException("Error: HTTP " + responseCode);
}

if (0 == httpConn.getContentLength()) {
Debug.info("Nothing to be downloaded " + fileURL);
return;
}
inChannel = Channels
.newChannel(new BufferedInputStream(httpConn.getInputStream()));
outChannel = Channels
.newChannel(new BufferedOutputStream(outputStream));
ByteBuffer buf = ByteBuffer.allocate(bufSize);
while (-1 != inChannel.read(buf)) {
buf.flip();
outChannel.write(buf);
buf.clear();
}
} finally {
// 关闭指定的Channel以及HttpURLConnection
Tools.silentClose(inChannel, outChannel);
httpConn.disconnect();
}
}// downloadFile结束
}// FileDownloader结束
}

面试八--多线程(一)线程创建的四种方法_System_05

 

面试八--多线程(一)线程创建的四种方法_java_06

标签:Thread,--,创建,MyCallable,线程,new,多线程,public
From: https://blog.51cto.com/u_12834811/6038972

相关文章