Java基础篇——线程创建的4种方式总结

前言

java多线程创建的4种方式,是java多线程的基础,但是学久了容易忘记,这里做一下总结,把基本的示例模板,注意事项记录一下,便于以后翻阅。


创建线程

方式1 Thread

定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。调用线程对象的start()方法来启动该线程。

public class ThreadTest extends Thread{
	int i = 0;
	//重写run方法,run方法的方法体就是现场执行体
	public void run()
	{
		for(;i<100;i++){
		System.out.println(getName()+"  "+i);
		
		}
	}
	public static void main(String[] args)
	{
		for(int i = 0;i< 100;i++)
		{
			System.out.println(Thread.currentThread().getName()+"  : "+i);
			if(i==20)
			{
				new ThreadTest().start();
				new ThreadTest().start();
			}
		}
	}
 
}复制代码


方式2 Runnable 

  1. 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。  
  2. 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。 
  3. 调用线程对象的start()方法来启动该线程。

public class RunnableThreadTest implements Runnable
{
 
	private int i;
	public void run()
	{
		for(i = 0;i <100;i++)
		{
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
	}
	public static void main(String[] args)
	{
		for(int i = 0;i < 100;i++)
		{
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==20)
			{
				RunnableThreadTest rtt = new RunnableThreadTest();
				new Thread(rtt,"新线程1").start();
				new Thread(rtt,"新线程2").start();
			}
		}
 
	}
 
}

或者用 new Runnable()

public class RunnableThreadTest {
    
    public static void main(String[] args) {
        for(int i = 0;i < 100;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==20) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for(int i = 0;i <100;i++) {
                            System.out.println(Thread.currentThread().getName()+" "+i);
                        }
                    }
                }, "新线程1").start();
               
            }
        }
    }
}
复制代码


方式3 Callable + FutureTask

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。  
  2. 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。  
  3. 使用FutureTask对象作为Thread对象的target创建并启动新线程。  
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值 

public class CallableThreadTest implements Callable<Integer>
{
 
	public static void main(String[] args)
	{
		CallableThreadTest ctt = new CallableThreadTest();
		FutureTask<Integer> ft = new FutureTask<>(ctt);
		for(int i = 0;i < 100;i++)
		{
			System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
			if(i==20)
			{
				new Thread(ft,"有返回值的线程").start();
			}
		}
		try
		{
			System.out.println("子线程的返回值:"+ft.get());
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		} catch (ExecutionException e)
		{
			e.printStackTrace();
		}
 
	}
 
	@Override
	public Integer call() throws Exception
	{
		int i = 0;
		for(;i<100;i++)
		{
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
		return i;
	}
 
}

或者像 Runnable 一样,用new Callable


public class CallableThreadTest {
    public static void main(String[] args) {
        Callable<Integer> ctt = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int i = 0;
                for(;i<100;i++)
                {
                    System.out.println(Thread.currentThread().getName()+" "+i);
                }
                return i;
            }
        };
        FutureTask<Integer> ft = new FutureTask<>(ctt);

        for(int i = 0;i < 100;i++) {
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
            if(i==20) {
                new Thread(ft,"有返回值的线程").start();
            }
        }
        try {
            System.out.println("子线程的返回值:"+ft.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

    }
}

复制代码


方式4 ThreadPoolExecutor线程池

在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。

public class ThreadPool {

    private static ExecutorService pool;

    public static void main( String[] args ) throws ExecutionException, InterruptedException {
        //自定义拒绝策略
        pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5),
                Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println(r.toString()+"执行了拒绝策略");
            }
        });
//  执行任务  execute或submit
        for(int i=0;i<10;i++) {
            //----------------execute---------------------
            pool.execute(new ThreadTask()); 
            // 或用new Runnable
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //让线程阻塞,使后续任务进入缓存队列
                        Thread.sleep(1000);
                        System.out.println("ThreadName:"+Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            // 或用lambda
            pool.execute(() ->{
                // .....
                // .....
            });
            
            // -------------------------submit---------------------------------
            Future<String> future = pool.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return "xixi,I am Ying";
                }
            });
            // 从future 中获取String返回值
            System.out.println(future.get());
           
            // 或用lambda
            Future<String> future2 = pool.submit(() ->{
                return "haha, I am Ying";
            });
            System.out.println(future2.get());

        }
    }
}

class ThreadTask implements Runnable{
    public void run() {
        try {
            //让线程阻塞,使后续任务进入缓存队列
            Thread.sleep(1000);
            System.out.println("ThreadName:"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}复制代码




创建线程方式的对比

1. 采用实现Runnable、Callable接口的方式创见多线程时,优势是:

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。


2. 使用继承Thread类的方式创建多线程时优势是:

编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

线程类已经继承了Thread类,所以不能再继承其他父类。


猜你喜欢

转载自juejin.im/post/5eaa8c816fb9a0432c4c9061