美文网首页java多线程
java实现多线程的三种方式

java实现多线程的三种方式

作者: ryderchan | 来源:发表于2017-02-19 13:08 被阅读262次

    原帖地址:原帖
    个人网站地址:个人网站


    三种方式

    1. 通过继承 Thread 类本身;
    2. 通过实现 Runnable 接口;
    3. 通过 Callable 和 Future 创建线程。

    1,2无返回值,3返回future对象。(其实Thread中的run方法调用的是Runnable接口的run方法。Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。)

    2相对于1灵活性更好,因为接口可以多实现,同时还可以继承其他类,而1无法完成;而且2方便共享资源:同一份资源,多个代理访问。

    直接集成Thread类

    在Java中负责线程这个功能的是Java.lang.Thread这个类。 可以通过创建Thread的实例来创建新的线程。 每个线程都是通个某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。 通过调用Thread类的start()方法来启动一个线程。

    代码大致框架如下:

    class 类名 extends Thread{
        属性1;
        属性2;
        …
        方法1;
        方法2;
        …
        public void run(){
        // other code…
        }
    }
    

    一个小例子,每个线程进行5次打印。

    package thread;
    
    class TestThread extends Thread {  
        public void run() {  
            for (int i = 0; i < 100; i++) {  
                if (count > 0) {  
                    System.out.println("count= " + count--);  
                }
                else{
                    break;
                }
            }  
        }  
        public static void main(String[] args) {  
            TestThread h1 = new TestThread();  
            TestThread h2 = new TestThread();  
            TestThread h3 = new TestThread();  
            h1.start();  
            h2.start();  
            h3.start();  
        }  
        private int count = 5;  
    }   
    

    某次的运行结果:

    h2: count= 5
    h2: count= 4
    h2: count= 3
    h2: count= 2
    h2: count= 1
    h1: count= 5
    h1: count= 4
    h1: count= 3
    h1: count= 2
    h1: count= 1
    h3: count= 5
    h3: count= 4
    h3: count= 3
    h3: count= 2
    h3: count= 1
    

    实现Runnable接口

    代码大致框架如下:

    class 类名 implements Runnable{
        属性1;
        属性2;
        …
        方法1;
        方法2;
        …
        public void run(){
        // other code…
        }
    }
    

    一个小例子,实现资源共享:三个卖票窗口,一起来卖5张票。

    package thread;
    
    /**
    实现Runnable接口比继承Thread类所具有的优势:
    1):适合多个【相同】的程序代码的线程去处理【同一】个资源
    2):可以跨过java中的单继承的限制,接口可以多实现
     */
    class TestRunnable implements Runnable {
        private int ticket = 5; // 5张票
    
        public void run() {
            while(ticket>0){
                synchronized (this){
                    if(ticket>0){
                        String nameString = Thread.currentThread().getName();
                        System.out.println(name + "正在卖票" + this.ticket--);
                        try {
                            Thread.sleep((int) Math.random() * 10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } 
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            TestRunnable t = new TestRunnable();
            new Thread(t, "1号窗口").start();
            new Thread(t, "2号窗口").start();
            new Thread(t, "3号窗口").start();
        }
    
    }
    

    某次的运行结果:

    1号窗口正在卖票5
    3号窗口正在卖票4
    2号窗口正在卖票3
    2号窗口正在卖票2
    3号窗口正在卖票1
    

    实现Callable接口

    Callable和Runnable的不同

    Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable接口的类都是可被其它线程执行的任务 。Callable和Runnable有几点不同:

    • Callable规定的方法是call(),而Runnable规定的方法是run()
    • call()方法可抛出异常,而run()方法不能抛出异常
    • Callable的任务执行后可返回值,运行Callable任务可得到一个Future对象,而Runnable的任务无返回值。Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。

    使用思路

    通过FutureTask对象

    创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

    使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

    调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

    package thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    public class TestCallable implements Callable<Integer> {
        
        @Override
        public Integer call() throws Exception {
            int i = 0;
            for (; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
            return i;
        }
    
        public static void main(String[] args) {
            TestCallable tCallable = new TestCallable();
            FutureTask<Integer> fTask = new FutureTask<Integer>(tCallable);
            for (int i = 0; i < 8; i++) {
                System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
                if (i == 3) {
                    new Thread(fTask, "有返回值的线程").start();
                }
            }
                try {
                    System.out.println("有返回值的线程的返回值:" + fTask.get());
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    
    }
    
    

    某次运行结果:

    main 的循环变量i的值0
    main 的循环变量i的值1
    main 的循环变量i的值2
    main 的循环变量i的值3
    main 的循环变量i的值4
    main 的循环变量i的值5
    main 的循环变量i的值6
    有返回值的线程 0
    main 的循环变量i的值7
    有返回值的线程 1
    有返回值的线程 2
    有返回值的线程 3
    有返回值的线程 4
    有返回值的线程 5
    有返回值的线程 6
    有返回值的线程 7
    有返回值的线程 8
    有返回值的线程 9
    有返回值的线程的返回值:10
    

    通过Executor

    Executor框架是Java5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好。参考:Executor框架与线程池

    创建:Callable实现类 +重写call

    借助ExecutorService:执行调度服务ExecutorService,获取Future对象

    ExecutorService ser = Executors.newFixedThreadPool(2);
    Future result = ser.submit(new CallableImpl()) (submit有返回值,execute无返回值)

    获取值:result.get()

    停止服务:ser.shutdownNow();

    package thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class TestCallable implements Callable<Integer> {
        private boolean flag = true;
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
        @Override
        public Integer call() throws Exception {
            int i = 0;
            while(flag){
                i++;
                System.out.println(Thread.currentThread().getName() + " " + i);
                Thread.yield();
            }
            return i;
        }
    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            TestCallable tCallable1 = new TestCallable();
            TestCallable tCallable2 = new TestCallable();
            
            ExecutorService executorService = Executors.newFixedThreadPool(3);
            Future<Integer> result1 = executorService.submit(tCallable1);
            Future<Integer> result2 = executorService.submit(tCallable1);
            
            Thread.sleep(500);
            tCallable1.setFlag(false);
            tCallable2.setFlag(false);
            
            System.out.println("tCallable1: "+result1.get());
            System.out.println("tCallable2: "+result2.get());   
            executorService.shutdownNow();
        }
    }
    

    某次运行的部分结果:

    ......
    pool-1-thread-1 12215
    pool-1-thread-1 12216
    pool-1-thread-2 14854
    pool-1-thread-2 14855
    pool-1-thread-2 14856
    pool-1-thread-2 14857
    pool-1-thread-2 14858
    pool-1-thread-2 14859
    pool-1-thread-2 14860
    pool-1-thread-2 14861
    pool-1-thread-2 14862
    pool-1-thread-2 14863
    pool-1-thread-2 14864
    pool-1-thread-2 14865
    pool-1-thread-2 14866
    pool-1-thread-2 14867
    pool-1-thread-2 14868
    pool-1-thread-2 14869
    pool-1-thread-1 12217
    tCallable1: 12217
    tCallable2: 14869
    

    相关文章

      网友评论

        本文标题:java实现多线程的三种方式

        本文链接:https://www.haomeiwen.com/subject/mzhjwttx.html