创建线程thread、runable、callable、ExecutorService 的四种方式

创建线程的四种方式

1.继承Thread类
2.实现Runnable类
3.实现Callable类
4.通过线程池创建线程: 提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度

体系结构:
java.util.concurrent.Executor : 负责线程的使用与调度的根接口
|–ExecutorService 子接口: 线程池的主要接口
|–ThreadPoolExecutor 线程池的实现类
|–ScheduledExecutorService 子接口:负责线程的调度
|–ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService *

工具类 : Executors
ExecutorService newFixedThreadPool() : 创建固定大小的线程池
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。

具体如下:

一、继承Thread类

继承Thread类创建线程的步骤为:
(1)创建一个类继承Thread类,重写run()方法,将所要完成的任务代码写进run()方法中;
(2)创建Thread类的子类的对象;
(3)调用该对象的start()方法,该start()方法表示先开启线程,然后调用run()方法;

public class ThreadTest {
    public static void main(String[] args) {
        window t1 = new window();
        window1 t2 = new window1();
        t1.setName("售票口1");
        t2.setName("售票口2");
        t1.start();
        t2.start();
        if(t1.isInterrupted() == true){
            t1.interrupt();
            System.out.println("线程1停止");
        }
    }
    static class window extends Thread{
       //将其加载在类的静态区,所有线程共享该静态变量
        private static int ticket = 10; 
        @Override
        public synchronized void run() {
            while(true){
                if(ticket>0){
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+"当前售出第"+ticket+"张票");
                    ticket--;
                }else{
                    interrupt();
                    System.out.println("1正在判断线程是否终止"+isInterrupted());
                    break;
                }
                System.out.println("1正在判断线程是否终止@@"+isInterrupted());
            }
        }
    }

   static class window1 extends Thread{
     //将其加载在类的静态区,所有线程共享该静态变量
        private static int ticket = 5; 
        @Override
        public synchronized void run() {
            while(true){
                if(ticket>0){
                    System.out.println(getName()+"当前售出第"+ticket+"张票");
                    try {
                        sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                }else{
                    interrupt();
                    System.out.println("2正在判断线程是否终止"+isInterrupted());
                    break;
                }
                System.out.println("2正在判断线程是否终止"+isInterrupted());
            }
        }
    }
 }

thread修饰符、返回类型、方法和说明:

修饰符、返回类型、方法和说明
static int	activeCount()
返回当前线程的线程组及其子组中活动线程的数量的估计值。
void	checkAccess()
确定当前正在运行的线程是否有权修改此线程。
protected Object	clone()
抛出CloneNotSupportedException,因为无法有意义地克隆线程。
int	countStackFrames()
不推荐使用。 
此调用的定义取决于suspend()已弃用的。此外,此调用的结果从未明确定义。
static Thread	currentThread()
返回对当前正在执行的线程对象的引用。
void	destroy()
不推荐使用。 
此方法最初设计为在不进行任何清理的情况下破坏此线程。它所持有的任何监视器都将保持锁定状态。但是,该方法从未实现。如果要实施,那么它在很大程度上将很容易死锁suspend()。如果目标线程在销毁关键系统资源时持有锁来保护它,则任何线程都无法再次访问该资源。如果另一个线程曾试图锁定此资源,则将导致死锁。这种僵局通常表现为“冻结”进程。有关更多信息,请参见 为什么不赞成使用Thread.stop,Thread.suspend和Thread.resume?。
static void	dumpStack()
将当前线程的堆栈跟踪记录打印到标准错误流。
static int	enumerate(Thread[] tarray)
将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。
static Map<Thread,StackTraceElement[]>	getAllStackTraces()
返回所有活动线程的堆栈跟踪的映射。
ClassLoader	getContextClassLoader()
返回此线程的上下文ClassLoader。
static Thread.UncaughtExceptionHandler	getDefaultUncaughtExceptionHandler()
返回由于未捕获的异常导致线程突然终止时调用的默认处理程序。
long	getId()
返回此线程的标识符。
String	getName()
返回此线程的名称。
int	getPriority()
返回此线程的优先级。
StackTraceElement[]	getStackTrace()
返回表示该线程的堆栈转储的堆栈跟踪元素的数组。
Thread.State	getState()
返回此线程的状态。
ThreadGroup	getThreadGroup()
返回该线程所属的线程组。
Thread.UncaughtExceptionHandler	getUncaughtExceptionHandler()
返回此线程由于未捕获的异常而突然终止时调用的处理程序。
static boolean	holdsLock(Object obj)
当且仅当当前线程在指定对象上持有监视器锁时,才返回true。
void	interrupt()
中断此线程。
static boolean	interrupted()
测试当前线程是否已被中断。
boolean	isAlive()
测试此线程是否仍然存在。
boolean	isDaemon()
测试此线程是否是守护程序线程。
boolean	isInterrupted()
测试此线程是否已被中断。
void	join()
等待该线程死亡。
void	join(long millis)
等待最多millis毫秒,直到该线程消失。
void	join(long millis, int nanos)
等待最多millis毫秒加 nanos十亿分之一秒的时间,以使该线程死亡。
void	resume()
不推荐使用。 
该方法仅适用于与suspend(),因为它容易死锁,因此已弃用。有关更多信息,请参见 为什么不赞成使用Thread.stop,Thread.suspend和Thread.resume?。
void	run()
如果此线程是使用单独的 Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,将调用该 对象的方法。否则,此方法不执行任何操作并返回。
void	setContextClassLoader(ClassLoader cl)
设置此线程的上下文ClassLoader。
void	setDaemon(boolean on)
将此线程标记为守护程序线程或用户线程。
static void	setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当线程由于未捕获的异常而突然终止并且没有为该线程定义其他处理程序时调用的默认处理程序。
void	setName(String name)
将此线程的名称更改为等于参数 name。
void	setPriority(int newPriority)
更改此线程的优先级。
void	setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当此线程由于未捕获的异常突然终止时调用的处理程序。
static void	sleep(long millis)
根据系统计时器和调度程序的精度和准确性,使当前正在执行的线程进入休眠状态(暂时停止执行)达指定的毫秒数。
static void	sleep(long millis, int nanos)
根据系统计时器和调度程序的精度和准确性,使当前正在执行的线程进入休眠状态(暂时停止执行)达指定的毫秒数加上指定的纳秒数。
void	start()
使该线程开始执行;Java虚拟机将调用run此线程的方法。
void	stop()
不推荐使用。 
这种方法本质上是不安全的。使用Thread.stop停止线程会导致它解锁所有已锁定的监视器(由于未经检查的ThreadDeath异常会自然 传播堆栈)。如果先前由这些监视器保护的任何对象处于不一致状态,则损坏的对象将变为其他线程可见,从而可能导致任意行为。的许多用途stop应该用简单地修改一些变量以指示目标线程应该停止运行的代码代替。目标线程应定期检查此变量,如果该变量指示它将停止运行,则应有序地从其运行方法返回。如果目标线程等待很长时间(例如,在条件变量上),interrupt则应使用该方法来中断等待。有关更多信息,请参见 为什么不赞成使用Thread.stop,Thread.suspend和Thread.resume?。
void	stop(Throwable obj)
不推荐使用。 
这种方法本质上是不安全的。有关stop() 详细信息,请参见。此方法的另一个危险是,它可能会用于生成目标线程不准备处理的异常(包括如果该方法不支持该线程,则可能无法抛出的已检查异常)。有关更多信息,请参见 为什么不赞成使用Thread.stop,Thread.suspend和Thread.resume?。
void	suspend()
不推荐使用。 
此方法已被弃用,因为它固有地容易死锁。如果目标线程在挂起时在监视器上持有锁以保护关键系统资源,则在恢复目标线程之前,没有线程可以访问该资源。如果将恢复目标线程的线程在调用之前尝试锁定此监视器resume,则会导致死锁。这种僵局通常表现为“冻结”进程。有关更多信息,请参见 为什么不赞成使用Thread.stop,Thread.suspend和Thread.resume?。
String	toString()
返回此线程的字符串表示形式,包括线程的名称,优先级和线程组。
static void	yield()
向调度程序提示当前线程愿意放弃当前使用的处理器。

二、实现Runnable类

实现Runnable类创建线程的步骤为:
(1)创建一个类实现Runnable类,重写run()方法,将所要完成的任务代码写进run()方法中;
(2)创建Runnable类的子类的对象;
(3)需要首先实例化一个Thread,并传入自己的MyRunnable实例
(4)调用该对象的start()方法,该start()方法表示先开启线程,然后调用run()方法;

public class TestRunnable {
    public static void main(String[] args) {
		MyRunnable myRunnable = new MyRunnable ();
		new Thread(myRunnable).start();
	}
}
 
class MyRunnable implements Runnable{
	@Override
	public void run() {
		boolean flag = false;
		for(int i  = 3 ; i < 100 ; i ++) {
			flag = false;
			for(int j = 2; j <= Math.sqrt(i) ; j++) {
				if(i % j == 0) {
					flag = true;
					break;
				}
			}
			if(flag == false) {
				System.out.print(i+"  ");
			}
		}
	}

运行结果:

3  5  7  11  13  17  19  23  29  31  37  41  43  47  53  59  61  67  71  73  79  83  89  97

所有已知的子接口:
RunnableFuture ,RunnableScheduledFuture
V-此Future的get方法返回的结果类型

也可以直接ExecutorService的 d executor.execute(new RunnableTask1())启动线程

三、实现Callable类

实现Callable类创建线程的步骤为:
(1)创建一个类实现Callable类,重写run()方法,将所要完成的任务代码写进run()方法中,可以有返回值的线程;
(2)创建Callable类的子类的对象;
(3)需要首先实例化一个FutureTask ,并传入自己的myCallable实例
(4)需要首先实例化一个Thread,并传入自己的FutureTask 实例
(5)调用该对象的start()方法,该start()方法表示先开启线程,然后调用run()方法;

好处:可以调用FutureTask的方法
get() 获取返回值,那么因为这个方法是阻塞的,一直等待线程运行完成,有时需要等很久。所以有时候需要设置超时时间。
get(long var1, TimeUnit var3) 这个方法的第一个参数是长整形数字,第二个参数是单位
cancel(boolean mayInterruptIfRunning) 取消任务,
传true,会取消正在运行的任务,取消成功返回true,失败返回false;
传false,只取消未启动的任务,正在运行的任务会正常运行成功。
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
isDone方法表示任务是否已经完成,若任务完成,则返回true;

public class TestCallable {
    public static void main(String[] args) {

        MyCallable myCallable = new MyCallable();
        //FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。
        FutureTask futureTask = new FutureTask(myCallable);
        Thread thread =new Thread(futureTask);
        thread.start();
    }

    static class MyCallable implements Callable{

        List<Object> list = new ArrayList<>();
        @Override
        public Object call() throws Exception {
            boolean flag = false;
            for(int i  = 3 ; i < 100 ; i ++) {
                flag = false;
                for(int j = 2; j <= Math.sqrt(i) ; j++) {
                    if(i % j == 0) {
                        flag = true;
                        break;
                    }
                }
                if(flag == false) {
                    System.out.print(i+"  ");
                   list.add(i);
                }
            }
            return list;
        }
    }
}

运行结果:

3  5  7  11  13  17  19  23  29  31  37  41  43  47  53  59  61  67  71  73  79  83  89  97

四、创建线程池的方式

线程池创建线程的步骤为:
(1)首先创建一个线程池的ExecutorService,
(2)实例化一个Callable对象,并重写call()方法,可以有返回值;
(3)调用 executorService.submit(callable)这个方法,返回一个Future对象,用Future可以来接收多线程的执行结果。
或者executorService.execute(myRunnale)方法,返回一个Future对象,用Future可以来接收多线程的执行结果。

好处:可以调用Future的方法
get() 获取返回值,那么因为这个方法是阻塞的,一直等待线程运行完成,有时需要等很久。所以有时候需要设置超时时间。
get(long var1, TimeUnit var3) 这个方法的第一个参数是长整形数字,第二个参数是单位
cancel(boolean mayInterruptIfRunning) 取消任务,
传true,会取消正在运行的任务,取消成功返回true,失败返回false;
传false,只取消未启动的任务,正在运行的任务会正常运行成功。
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
isDone方法表示任务是否已经完成,若任务完成,则返回true;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                System.out.println("线程执行中..........");
                return "执行完毕";
            }
        };
        System.out.println("线程执行之前 "+TimeFormat());
        Future<String> future = executorService.submit(callable);
        System.out.println("线程开启之后 "+TimeFormat());
        //get方法会形成阻塞
//        System.out.println("获取返回值"+ future.get());
        try {
            future.get(2,TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            e.printStackTrace();
            System.out.println("时间超时,舍弃。。。。");
        }
        System.out.println("获取结果之后 "+TimeFormat());
        System.out.println("线程是否结束1 "+future.isDone());
        System.out.println("线程是否完成前被取消1 "+future.isCancelled());
        Boolean oo = future.cancel(true);
        System.out.println("线程是否结束2 "+oo);
        System.out.println("线程是否结束3 "+future.isDone());
        System.out.println("线程是否完成前被取消3 "+future.isCancelled());
    }
    
    private static String TimeFormat(){
        Date now = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
        return simpleDateFormat.format(now);
    }
}

运行结果:

线程执行之前 16:35:52
线程开启之后 16:35:52
java.util.concurrent.TimeoutException
	at java.util.concurrent.FutureTask.get(FutureTask.java:205)
	at CallableTest.main(CallableTest.java:24)
时间超时,舍弃。。。。
获取结果之后 16:35:55
线程是否结束1 false
线程是否完成前被取消1 false
线程是否结束2 true
线程是否结束3 true
线程是否完成前被取消3 true
线程是否结束4 false

TimeoutException 时间超时,是因为get()方法中需要设置了超时时间2秒,线程休眠3秒,所以直接舍弃,不返回结果。

Future是一个接口, Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。
所有的方法最终都将runnable或者callable转变成一个RunnableFuture的对象,这个RunnableFutre的对象是一个同时继承了Runnable和Future的接口

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

然后调用executor(runnable)方法,。最后返回一个RunnableFuture对象。RunnableFuture这个接口直有一个具体的实现类,

public class FutureTask<V> implements RunnableFuture<V>

FutureTask实现了RunnableFuture的接口,既然我们知道最终返回的是一个FutureTask对象ftask,而且我们可以通过ftask.get()可以的来得到execute(task)的返回值。

线程池四种实现方式及原理

不足之处,多多指教!!!

猜你喜欢

转载自blog.csdn.net/qq_41587243/article/details/105935364