Java线程的创建
方式一:
1.创建一个继承于Thread类的子类
2.重写Thread类的run()
3.创建Thread类的子类对象
4.通过此对象调用start()
public class ThreadTest {
public static void main(String[] args) {
//创建Thread类的子类的对象
MyThread mt = new MyThread();
//调用此对象的start():1.启动当前线程;2.调用当前线程的run();3.start()方法在一个对象中只能调用一次
mt.start();
//重复使用线程中的方法,不能重复调用一个对象中的start,只能重新new一个对象,起用线程
MyThread mt1 = new MyThread();
mt1.start();
//不能直接调用run()方法启动线程
//mt.run()//还是在主线程中执行
//两个线程,以下代码仍然再main()中执行
System.out.println("Hello World");//先输出Hello World,再执行mt.start()
//创建Thread类的匿名子类的方式(只用一次)
new Thread(){
@Override
public void run(){
for (int i = 0; i <100 ; i++) {
if(i % 2 != 0){
System.out.println(i);
}
}
}
}.start();
}
}
class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 100; i++){
if(i%2 == 0){
System.out.println(i);
}
}
}
}
方式二:实现Runnable接口
1.创建一个实现了Runnable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()
public class ThreadTest {
public static void main(String[] args) {
//3.创建实现类的对象
MThread mt1 = new MThread();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(mt1);
//5.通过Thread类的对象调用start():1.启动线程;2.调用当前线程的run()-->调用了Runnable类型的target的run(),即mt1的run()
t1.start();
//再启动一个线程,遍历100以内的偶数
Thread t2 = new Thread(mt1);
t2.start();
}
}
class MThread implements Runnable{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+ ":" + i);
}
}
}
}
开发中一般优先选择第二种实现Runnable接口的方式。
1.实现的方式没有类的继承性的局限性;
2.实现的方式更适合处理多个线程有共享数据的情况。
方式三:实现Callable接口
Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式: 实现 Callable 接口。
Callable 接口类似于 Runnable,但是 Runnable 不会返回结果,并且无法抛出经过检查的异常,而 Callable 依赖 FutureTask 类获取返回结果。
步骤:
1.创建一个实现Callable的实现类;
2.实现call方法,将线程需要做的操作声明在call方法中;
3.需要创建Callable接口实现类的对象;
4.将此Callable接口实现类的对象作为参数传递到FutureTask的创建对象中;
5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象并start();
6.可选择性获取Callable方法中的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author zane
* @create 2023-01-06-14:50
*
* 创建线程的方式三:实现Callable接口 jdk5.0新增
*
* 1.创建一个实现Callable的实现类
* 2.实现call方法,将线程需要做的操作声明在call方法中
* 3.需要创建Callable接口实现类的对象
* 4.将此Callable接口实现类的对象作为参数传递到FutureTask的创建对象中
* 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象并start()
* 6.可选择性获取Callable方法中的返回值
*
* Callable和Runnable接口对比
* 1.call()有返回值
* 2.call()可以抛出异常
* 3.Callable支持泛型
*/
public class ThreadNewtest {
public static void main(String[] args) {
NumThread numthread = new NumThread();
FutureTask futuretask = new FutureTask(numthread);
new Thread(futuretask).start();
try{
//get方法的返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值
Object sum = futuretask.get();
System.out.println("Total sum is: " + sum);
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
}
}
class NumThread implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
Callable和Runnable接口对比
1.call()有返回值
2.call()可以抛出异常
3.Callable支持泛型
方式四:线程池创建
JAVA中使用ThreadPoolExecutor来创建线程池。
步骤:
1.提供指定的数量的线程池
2.执行指定的线程的操作,需提供实现Runnable或Callable接口实现类的对象
3.关闭连接池
优势:
1.提高响应速度,减少了线程创建的时间
2.降低了资源消耗,重复利用线程池中的线程
3.便于对线程进行管理:
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持的时间
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author zane
* @create 2023-01-05-16:37
* 线程创建方式四:线程池创建
* 1.提供指定的数量的线程池
* 2.执行指定的线程的操作,需提供实现Runnable或Callable接口实现类的对象
* 3.关闭连接池
*
* 优势:
* 1.提高响应速度,减少了线程创建的时间
* 2.降低了资源消耗,重复利用线程池中的线程
* 3.便于对线程进行管理:
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持的时间
*
*/
public class ConcurrentTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
//executor类别:executor.getClass()
ThreadPoolExecutor executor1 = (ThreadPoolExecutor) executor;
//设置线程池属性
executor1.setCorePoolSize(3);
// executor1.setKeepAliveTime(200);
// Semaphore semaphore = new Semaphore(3);//配置只能发布3个运行许可证
for (int i = 0; i < 3; i++) {
executor1.execute(new Con());//仅创建,没有实现线程同步
}
//execute适用于Runnable
//executor.execute(new Con());
//submit适合用Callable
// executor.submit(new Con());
executor1.shutdown();
}
}
class Con implements Runnable{
private static int ticket = 100;
//还未解决线程池同步的问题
@Override
public void run(){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ": Tickets are selling,ticket id: " + ticket);
ticket--;
} else {
System.out.println("Tickets were sold out.");
break;
}
}
}
}
通过Executors.newFixedThreadPool()来创建线程池,会存在一个无界队列,将未操作的任务放入其中,随着队列中任务的增加,可能会爆内存。详细可以参考这篇blog:https://blog.csdn.net/qq_40096897/article/details/121660747
因此一些公司会限制该方法创建线程池,并且,阿里Java开发手册中强制规定,通过ThreadPollExecutor的方式创建线程池。
public static ExecutorService newThreadPool(int threadNum){
return new java.util.concurrent.ThreadPoolExecutor(threadNum,threadNum,
0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}