首页 > 代码库 > Java编程思想 第21章 并发

Java编程思想 第21章 并发

这是在2013年的笔记整理。现在重新拿出来,放在网上,重新总结下。

两种基本的线程实现方式 以及中断

 

package thread;

 

 

/**

*

* @author zjf

* @create_time 2013-12-18

* @use 测试基本的两种线程的实现方式

*         测试中断

*/

public class BasicThreadTest {

 

    public static void main(String[] args) {

        Counter c1 = new Counter();

        c1.start();

        Thread c2 = new Thread(new Countable());

        c2.start();

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        //中断

        c1.interrupt();

        c2.interrupt();

          

        

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        //此时c1线程已经终止 不能再次start 多次启动一个线程是非法的。java.lang.IllegalThreadStateException

        c1.start();

    }

      

    

    /**

     *

     * @author zjf

     * @create_time 2013-12-18

     * @use Runnable接口方式的实现

     */

    static class Countable implements Runnable{

        public void run() {

            int i = 0;

            while(!Thread.currentThread().isInterrupted())

            {

                System.out.println(this.toString() + "-------------" + i);

                i ++;

            }

        }

    }

    /**

     *

     * @author zjf

     * @create_time 2013-12-18

     * @use Thread继承方式的实现

     */

    static class Counter extends Thread{

        public void run() {

            int i = 0;

            while(!Thread.currentThread().isInterrupted())

            {

                System.out.println(this.toString() + "-------------" + i);

                i ++;

            }

        }

    }

}

中断
  • public void interrupt()

中断线程。

如果线程在调用 Object 类的 wait()wait(long)wait(long, int) 方法,或者该类的 join()join(long)join(long, int)sleep(long)sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException

  • public static boolean interrupted()

测试当前线程是否已经中断。线程的中断状态 由该方法清除。

  • public boolean isInterrupted()

测试线程是否已经中断。线程的中断状态 不受该方法的影响。

测试睡眠被中断

sleep是静态方法。

 

package thread;

 

 

/**

*

* @author zjf

* @create_time 2013-12-18

* @use 测试Sleep方法被中断

*/

public class SleepTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-18

     * @use 测试目的:睡眠时是否可以被中断

     * @param args

     */

    public static void main(String[] args) {

        Thread t = new Thread(new Sleepable());

        t.start();

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        t.interrupt();

    }

 

    

    static class Sleepable implements Runnable{

        public void run() {

            try {

                //睡眠10 但是线程开始1秒后被中断 当前线程在睡眠时能够接收到中断 然后退出

                Thread.sleep(10000);

            } catch (InterruptedException e) {

                //如果被中断 就退出

                System.out.println("exit");//一秒后退出

            }

        }

    }

}

 

测试使用yield让步

yield是静态方法。

 

package thread;

 

public class YieldTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-18

     * @use 测试yield方法

     * @param args

     */

    public static void main(String[] args) {

        new Thread() {

            @Override

            public void run() {

                for(int i = 1; i < 100; i++)

                {

                    System.out.println("yield-----" + i);

                    //测试结果显示 使用yield让步与不使用差别不大

                    Thread.yield();

                }

            }

        }.start();

        

        new Thread() {

            @Override

            public void run() {

                for(int i = 1; i < 100; i++)

                {

                    System.out.println("notyield-----" + i);

                }

            }

        }.start();

    }

 

}

 

测试cached线程池

 

newCachedThreadPool:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

 

CachedThreadPool一般会创建所需数量的线程,并且会复用,这是选择的首选。

 

package thread;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class CachedThreadPoolTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-18

     * @use 测试Cached线程池

     * @param args

     */

    public static void main(String[] args) {

 

        /*

         * cached线程池不能设置拥有线程的数量

         */

        ExecutorService es = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {

            es.execute(new Countable(i));

        }

        

        /*

         * 因为要复用线程 所以线程执行完任务之后不会立刻关闭 而是等待一分钟(可配置)

         * 的时间 如果在这一分钟期间没有新的任务要执行 会自动关闭

         * shutdown标明不会再有新的任务加入 所以加入shutdown代码之后任务执行之后就会关闭线程

         * 不会等待一分钟

         */

        es.shutdown();

 

    }

 

    static class Countable implements Runnable {

 

        private int i;

 

        public Countable(int i) {

            this.i = i;

        }

 

        public void run() {

 

            System.out.println("" + i + "启动的线程的ID"

                    + Thread.currentThread().getId());

            

            /**

             * 输出为

                 0启动的线程的ID7

                2启动的线程的ID9

                1启动的线程的ID8

                3启动的线程的ID10

                4启动的线程的ID11

                5启动的线程的ID12

                6启动的线程的ID13

                8启动的线程的ID8

                7启动的线程的ID9

                9启动的线程的ID10

                

                可见 在地8 9 10个线程的时候 复用了第1 2 3个线程。

                这建立在第1 2 3个线程已经运行完的基础上。

             */

 

        }

    }

 

}

shutdown和shutdownnow

shutdown:

  • 阻止加入新的任务。
  • 结束已经完成任务的空闲线程,直到所有任务执行完毕,关闭所有线程为止。

shutdownnow:

  1. 完成shutdown的功能。
  2. 向每个未完成的线程发布中断命令。
  3. 返回未执行的任务列表
shutdownnow

package thread;

 

import java.util.List;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

 

public class ShutdownNowTest {

 

    /**

     * @author zjf

     * @create_time 2014-2-18

     * @use

     * @param args

     * @throws InterruptedException

     */

    public static void main(String[] args) throws InterruptedException {

 

        ExecutorService es = Executors.newFixedThreadPool(3);

        for(int i = 0; i < 10; i ++)

        {

            es.execute(new Countable(i));

        }

        TimeUnit.SECONDS.sleep(1);

        //返回等待的任务列表

        List<Runnable> countList = es.shutdownNow();

        for(Runnable r : countList)

        {

            System.out.println(r.toString() + " is not done...");

        }

    }

 

}

 

class Countable implements Runnable{

    private int i;

    public Countable(int i) {

        this.i = i;

    }

    

    public int getI() {

        return i;

    }

 

    @Override

    public String toString() {

        

        return "thread, id : " + i;

    }

    

    public void run() {

        

        try {

            System.out.println(this.toString() + " is start to run...");

            TimeUnit.MILLISECONDS.sleep(500);

            System.out.println(this.toString() + " is done...");

        } catch (InterruptedException e) {

            System.out.println(this.toString() + " is interrupted...");

        }

    }

    

}

 

 

/** 输出

 

thread, id : 0 is start to run...

thread, id : 1 is start to run...

thread, id : 2 is start to run...

thread, id : 0 is done...

thread, id : 1 is done...

thread, id : 2 is done...

thread, id : 3 is start to run...

thread, id : 4 is start to run...

thread, id : 5 is start to run...

thread, id : 5 is done...

thread, id : 3 is done...

thread, id : 4 is done...

thread, id : 6 is start to run...

thread, id : 6 is interrupted...

thread, id : 7 is not done...

thread, id : 8 is not done...

thread, id : 9 is not done...

 

*/

测试ThreadFactory

package thread;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.ThreadFactory;

 

public class ThreadFactoryTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-18

     * @use 测试Cached线程池

     * @param args

     */

    public static void main(String[] args) {

 

        ThreadFactory threadFactory = new MyThreadFactory();

        ExecutorService es = Executors.newCachedThreadPool(threadFactory);

        for (int i = 0; i < 10; i++) {

            es.execute(new Countable(i));

        }

        es.shutdown();

    }

 

    static class Countable implements Runnable {

 

        private int i;

        public Countable(int i) {

            this.i = i;

        }

        public void run() {

            System.out.println("" + i + "个任务正在运行!");

        }

    }

 

    static class MyThreadFactory implements ThreadFactory {

        private static int count = 0;

        public Thread newThread(Runnable r) {

            return new MyThread(r,count++);

        }

    };

    

    static class MyThread extends Thread

    {

        private Runnable target;

        private int count;

        public MyThread(Runnable target, int count) {

            super();

            this.target = target;

            this.count = count;

        }

        @Override

        public void run() {

            System.out.println("" + count + "个线程启动!" );

            if(target != null)

            {

                target.run();

            }

            System.out.println("" + count + "个线程结束!" );

        }

    }

}

/*

* 输出结果

        0个线程启动!

        1个线程启动!

        2个线程启动!

        3个线程启动!

        0个任务正在运行!

        1个任务正在运行!

        2个任务正在运行!

        4个线程启动!

        3个任务正在运行!

        5个线程启动!

        4个任务正在运行!

        5个任务正在运行!

        8个任务正在运行!

        6个线程启动!

        7个任务正在运行!

        7个线程启动!

        6个任务正在运行!

        9个任务正在运行!

        7个线程结束!

        0个线程结束!

        3个线程结束!

        6个线程结束!

        5个线程结束!

        1个线程结束!

        4个线程结束!

        2个线程结束!

        

    证明:    Countable中的run方法被执行了10

            MyThread中的run方法只被执行了9

            原因:CachedThreadPool在需要的时候会调用ThreadFactorynewThread方法 但是也会用到缓存

            */

测试FixedThreadPool

 

newFixedThreadPool:创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。(这与cacheThreadPool不一样)

 

 

 

package thread;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class FixedThreadPoolTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-18

     * @use 测试Fixed线程池

     * @param args

     */

    public static void main(String[] args) {

 

        

        ExecutorService es = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {

            es.execute(new Countable(i));

        }

        es.shutdown();

 

    }

 

    static class Countable implements Runnable {

 

        private int i;

 

        public Countable(int i) {

            this.i = i;

        }

 

        public void run() {

 

            System.out.println("" + i + "启动的线程的ID"

                    + Thread.currentThread().getId());

 

        }

    }

 

}

/*

0启动的线程的ID7

2启动的线程的ID9

1启动的线程的ID8

3启动的线程的ID7

4启动的线程的ID9

*/

 

SingleThreadExecutor

newSingleThreadExecutor():

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程(备注:应该是内部实现的差异 外部的使用没什么差异)。

 

因为一个任务执行完毕之后,线程才会空闲下来去执行另外一个任务,所以可以保证顺序执行任务。

测试ScheduledExecutorService

scheduled

adj. 预定的;已排程的

v. 把…列表;把…列入计划;安排(schedule的过去分词)

 

上面演示的线程执行器或者线程池都是ExecutorService,下面看看ScheduledExecutorService。ScheduledExecutorService集成并且扩展了ExecutorService,可安排在给定的延迟后运行或定期执行的命令。

 

package thread;

 

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

 

public class SingleThreadScheduledTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-23

     * @use 测试SingleThreadScheduled线程池

     * @param args

     * @throws InterruptedException

     */

    public static void main(String[] args) throws InterruptedException {

 

        ScheduledExecutorService es = Executors

                .newSingleThreadScheduledExecutor();

        //ScheduledThreadPool需要传参控制池中所保存的线程数(即使线程是空闲的也包括在内)

        //ScheduledExecutorService es = Executors.newScheduledThreadPool(1);

        // 给定时间延迟后执行

        // es.schedule(new Countable(), 1, TimeUnit.SECONDS);

        // 传入一个任务然后按照给定频率循环执行 在每次任务开始执行的时间点之间存在固定间隔

        //es.scheduleAtFixedRate(new Countable(), 2, 1, TimeUnit.SECONDS);

        // 传入一个任务然后按照给定频率循环执行 每一次执行终止和下一次执行开始之间都存在给定的间隔

        es.scheduleWithFixedDelay(new Countable(), 2, 1, TimeUnit.SECONDS);

        // 如果没有这句代码 将没有任何反应,因为----|

        // 下面的shutdown代码将会阻止执行新加入任务 包含延迟执行而未执行的任务

        TimeUnit.SECONDS.sleep(10);

        es.shutdown();

    }

 

    static class Countable implements Runnable {

 

        public void run() {

            System.out.println("一个任务运行开始!");

            try {

                TimeUnit.SECONDS.sleep(1);

            } catch (InterruptedException e) {

            }

            System.out.println("一个任务运行结束!");

        }

    }

 

}

 

package thread;

 

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

 

public class ScheduledThreadPoolTest {

 

    /**

     * @author zjf

     * @create_time 2013-12-23

     * @use 测试SingleThreadScheduled线程池

     * @param args

     * @throws InterruptedException

     */

    public static void main(String[] args) throws InterruptedException {

 

        //ScheduledThreadPool需要传参控制池中所保存的线程数(即使线程是空闲的也包括在内)

        ScheduledExecutorService es = Executors.newScheduledThreadPool(1);

        es.scheduleAtFixedRate(new Countable(), 0, 1, TimeUnit.SECONDS);

        TimeUnit.SECONDS.sleep(10);

        es.shutdown();

    }

 

    static class Countable implements Runnable {

 

        public void run() {

            System.out.println("一个任务运行开始!");

            try {

                TimeUnit.SECONDS.sleep(3);

            } catch (InterruptedException e) {

            }

            System.out.println("一个任务运行结束!");

        }

    }

}

/*

* 线程池中只有一个线程 + 每隔1秒要执行一个任务 + 一个任务要运行3秒才结束

* 结果是每隔3秒才能执行一次

*/

 

Java编程思想 第21章 并发