zoukankan      html  css  js  c++  java
  • Java多线程基础(一)

      一个简单的多线程的例子:

    package multiThread;
    
    public class BasicThread implements Runnable{
        
        private int countDown  = 10;
        private static int taskCount = 0;
        private final int id = taskCount++;
        
        public static void main(String [ ] args) {
            Thread t = new Thread(new BasicThread());
            t.setName("test_thread1");
            t.start();   //not t.run();  t.run() will not start a new thread,just exist one thread
            System.out.println("i am finished!");
        }
    
        @Override
        public void run() {
            while(countDown>=0){            
               System.out.print("#"+ id + "(" + (countDown>0 ? countDown : "lift off! "+ Thread.currentThread().getName()+ "
    ") + "),"); 
    countDown --; } } }

      运行的结果为:

    i am finished!
    #0(10),#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#0(lift off!),

      关于上面的代码有几点说明一下:

      1.final int id = taskCount++,final修饰基本变量时,值是不变的,从结果可以看到,id的值始终为0。

      2.使用t.start()来开启一个线程,而不是t.run(),t.run()还是运行在main方法的线程中,始终只有1个线程。

      3.使用t.setName("test_thread1");是一个好的习惯,方便后续的定位问题。

      使用setDaemon(true)可以将线程设置为后台线程,后台线程有如下的特点:

      1.JVM中只剩下Daemon线程时就会退出,只要还有一个non-Daemon线程存活,JVM就不会退出。Daemon线程可以在做一些后台的服务性工作,例如JVM的gc线程就是一个低优先级的Daemon线程。

      2.线程启动时,默认是non-Daemon的。

      3.setDaemon(true)一定要在start方法之前,否则回报异常。

      4.无法将一个已经启动的non-Daemon线程变为Daemon线程。

      5.Daemon线程中产生的新线程默认将是Daemon的。

      使用Executor来完成多线程,例子如下:

    package multiThread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ExecutorTest {
        
        public static void main(String [ ] args) {
            ExecutorService exec = Executors.newCachedThreadPool(); //will create one thread for each task 
            //ExecutorService exec2 = Executors.newFixedThreadPool(5); only eixsts 5 threads,others will wait
            //ExecutorService exec3 = Executors.newSingleThreadExecutor(); 
            for(int i=0;i<7;i++){
                exec.execute(new BasicThread());
            }
            //exec.shutdown();      programe will not exit
    //exec.execute(new BasicThread()); error exec.execute(new BasicThread()); exec.execute(new BasicThread()); } }

      所有的线程池在线程可用的情况下,都会去复用已创建的线程,上面的代码运行结果为:

    #0(10),#1(10),#0(9),#0(8),#0(7),#0(6),#0(5),#0(4),#0(3),#0(2),#0(1),#3(10),#3(9),#3(8),#3(7),#3(6),#3(5),#3(4),#3(3),#3(2),#3(1),#3(lift off! pool-1-thread-4
    ),#2(10),#0(lift off! pool-1-thread-1
    ),#1(9),#1(8),#1(7),#1(6),#1(5),#1(4),#1(3),#1(2),#1(1),#1(lift off! pool-1-thread-2
    ),#2(9),#2(8),#2(7),#2(6),#2(5),#2(4),#2(3),#2(2),#2(1),#2(lift off! pool-1-thread-3
    ),#7(10),#7(9),#7(8),#7(7),#7(6),#7(5),#7(4),#7(3),#7(2),#7(1),#6(10),#6(9),#6(8),#6(7),#6(6),#6(5),#6(4),#6(3),#6(2),#6(1),#4(10),#4(9),#4(8),#4(7),#4(6),#8(10),#6(lift off! pool-1-thread-2
    ),#7(lift off! pool-1-thread-4
    ),#5(10),#8(9),#8(8),#8(7),#8(6),#8(5),#8(4),#8(3),#4(5),#4(4),#4(3),#4(2),#4(1),#4(lift off! pool-1-thread-5
    ),#8(2),#8(1),#8(lift off! pool-1-thread-6
    ),#5(9),#5(8),#5(7),#5(6),#5(5),#5(4),#5(3),#5(2),#5(1),#5(lift off! pool-1-thread-1
    ),

       上面的例子有一点是需要注意的:

      注释掉exec.shutdown();之后,程序将在所有线程运行完成后一分钟后才结束。如果想要想线程运行完后,程序自动退出,需要加上exec.shutdown();

      exec.shutdown();的作用是为了防止新的任务加入线程队列中。

      如果希望线程在结束时可以带上返回值,可以使用Callable来代替Runnable,如下例: 

    package multiThread;
    
    import java.io.UnsupportedEncodingException;
    import java.util.ArrayList;
    import java.util.List;
    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 CallableTest {
    
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public static void main(String[] args) throws Exception{
            ExecutorService es = Executors.newCachedThreadPool();
            List<Future> ls = new ArrayList<Future>();
            for(int i =0;i<5;i++){
                ls.add(es.submit(new RuturnObj(i)));
            }
            es.shutdown();  //only with es.shutdown(),the program will shut down,unless program will not shut down
            for(Future f:ls){
                System.out.println(f.get());
            }
            
        }
    }
    
    @SuppressWarnings("rawtypes")
    class RuturnObj implements Callable{
        private int id;
        
        RuturnObj(int id){
            this.id = id;
        }
        
        @Override
        public Object call() throws Exception {        
            return "RuturnObj return id: " + id ;
        }
        
    }

      运行结果为:

    RuturnObj return id: 0
    RuturnObj return id: 1
    RuturnObj return id: 2
    RuturnObj return id: 3
    RuturnObj return id: 4

      在第一个线程上调用第二个线程的join方法,第一个线程将等待第二个线程完成后再接着执行。如果第一个线程不想等待,可以调用interrupt方法,第一个方法将继续执行,第二个方法将等待执行。

    package multiThread;
    
    public class JoinTest {
        
        public static void main(String [ ] args) {
            Sleeper sheep = new Sleeper("sheep"),pig = new Sleeper("pig");
            Joiner doc = new Joiner("doc",sheep),gru = new Joiner("gru",pig);
            
            gru.interrupt();
        }    
    }
    
    class Sleeper extends Thread{
        public Sleeper(String name){
            super(name);
            start();
        }
        
        public void run(){
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                System.out.println("Sleeper "  + getName() + " was interrupted!");
            }
            System.out.println("Sleeper " + getName() + " has finished!");
        }
    }
        
    
    class Joiner extends Thread{
        private Sleeper sl;
        public Joiner(String name,Sleeper sl){
            super(name);
            this.sl = sl;
            start();
        }
        public void run(){
            try {
                sl.join();
            }
            catch (InterruptedException e) {
                System.out.println("Joiner " + getName() + " was interrupted!");
            }
            System.out.println("Joiner " + getName() + " has finished!");
        }
        
    }

      输出结果为:

    Joiner gru was interrupted!
    Joiner gru has finished!
    Sleeper sheep has finished!
    Sleeper pig has finished!
    Joiner doc has finished!

      Java SE5中的concurrent包中的CyclicBarrier使用上比join更为合适。

      下面是一个使用CyclicBarrier的例子,开启了6个线程,模拟统计各省的信息,各省的信息都统计完后再统计全国的信息,如下:

    package multiThread;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class CyclicBarrierTest {
        //CountDownLatch: 一个或者是一部分线程 ,等待另外一部线程都完成了,再继续执行 
        //CyclicBarrier: 所有线程互相等待完成,再执行主任务 
        //一个是在主任务里面await,一个是在子任务里面await
        
        public static void main(String[] args) {
            TotalService totalService = new TotalServiceImpl();
            CyclicBarrier barrier = new CyclicBarrier(5, new TotalTask(totalService));
    
            List<String> provinceNames = Arrays.asList("北京","上海","广西","四川","黑龙江");        
            ExecutorService es = Executors.newCachedThreadPool();
            for(int i=0;i<provinceNames.size();i++){
                es.execute(new BillTask(new BillServiceImpl(), barrier, provinceNames.get(i)));
            }
            es.shutdown();        
        }
    }
    
    /**
     * 主任务:汇总任务
     */
    class TotalTask implements Runnable {
        private TotalService totalService;
    
        TotalTask(TotalService totalService) {
            this.totalService = totalService;
        }
    
        public void run() {
            // 读取内存中各省的数据汇总,过程略。
            System.out.println("=======================================");
            System.out.println("开始全国汇总");
            totalService.count();
            System.out.println("全国数据汇总完毕");
        }
    }
    
    /**
     * 子任务:计费任务
     */
    class BillTask extends Thread {
        // 计费服务
        private BillService billService;
        private CyclicBarrier barrier;
        // 代码,按省代码分类,各省数据库独立。
        private String code;
    
        BillTask(BillService billService, CyclicBarrier barrier, String code) {
            this.billService = billService;
            this.barrier = barrier;
            this.code = code;
        }
    
        public void run() {
            System.out.println("开始计算--" + code + "省--数据!");
            billService.bill(code);
            // 把bill方法结果存入内存,如ConcurrentHashMap,vector等,代码略
            System.out.println(code + "省已经计算完成,并通知汇总Service!");
            try {
                // 通知barrier已经完成
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
    
    interface BillService {
        public void bill(String code);
    }
    
    class BillServiceImpl implements BillService {
    
        @Override
        public void bill(String code) {
            Random rd = new Random(47);
            int i = rd.nextInt(4000);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    interface TotalService {
        public void count();
    }
    
    class TotalServiceImpl implements TotalService {
    
        @Override
        public void count() {
            Random rd = new Random(47);
            int i = rd.nextInt(4000);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

      输出结果为:

    开始计算--北京省--数据!
    开始计算--广西省--数据!
    开始计算--上海省--数据!
    开始计算--黑龙江省--数据!
    开始计算--四川省--数据!
    四川省已经计算完成,并通知汇总Service!
    北京省已经计算完成,并通知汇总Service!
    黑龙江省已经计算完成,并通知汇总Service!
    广西省已经计算完成,并通知汇总Service!
    上海省已经计算完成,并通知汇总Service!
    =======================================
    开始全国汇总
    全国数据汇总完毕

      CountDownLatch可以完成与CyclicBarrier类似的功能,CyclicBarrier是可以复用的,CountDownLatch却不可以。

      下面用CountDownLatch模拟项目的开发,只有当每个模块都完成后,项目才完成, 每个模块的用时不同。

    package multiThread;
    
    import java.util.Random;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    /**
     * 模拟项目的开发,只有当每个模块都完成后,项目才完成 每个模块的用时不同
     * 
     */
    public class CountDownLatchTest {
        private static final int SIZE = 5;
    
        public static void main(String[] args) {
            CountDownLatch latch = new CountDownLatch(SIZE);
            Random r = new Random(47);
            ExecutorService exec = Executors.newCachedThreadPool();
            Controller controller = new Controller(latch);
            exec.execute(controller);
            for (int i = 0; i < SIZE; i++) {
                exec.execute(new Module(latch, "模块" + (i + 1), r.nextInt(2000)));
            }
            exec.shutdown();
        }
    }
    
    class Module implements Runnable {
        private CountDownLatch latch;
        private String moduleName;
        private int time;// 用时
    
        public Module(CountDownLatch latch, String moduleName, int time) {
            super();
            this.latch = latch;
            this.moduleName = moduleName;
            this.time = time;
        }
    
        @Override
        public void run() {
            try {
                work();
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        private void work() throws InterruptedException {
            TimeUnit.MILLISECONDS.sleep(time);
            System.out.println(moduleName + " 完成,耗时:" + time);
        }
    }
    
    class Controller implements Runnable {
        private CountDownLatch latch;
    
        public Controller(CountDownLatch latch) {
            super();
            this.latch = latch;
        }
    
        @Override
        public void run() {
            try {
                latch.await();
                System.out.println("所有模块都完成,任务完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

      运行结果为:

    模块2 完成,耗时:555
    模块3 完成,耗时:693
    模块5 完成,耗时:961
    模块1 完成,耗时:1258
    模块4 完成,耗时:1861
    所有模块都完成,任务完成
  • 相关阅读:
    RADAR毫米波雷达传感器
    固态LiDAR,半固态混合LiDAR,机械LiDAR
    Lidar激光雷达市场
    echarts 环形图中自定义文字
    uni-app base64 无法显示问题
    实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
    实战一(下):如何实现一个遵从设计原则的积分兑换系统?
    实战一(上):针对业务系统的开发,如何做需求分析和设计?
    学而不记,不学无异 -- English learning
    springmvc 传入返回参数更改
  • 原文地址:https://www.cnblogs.com/lnlvinso/p/4542887.html
Copyright © 2011-2022 走看看