zoukankan      html  css  js  c++  java
  • 谈谈多线程

    package thread;
    /**
     * 线程
     * 线程可以并发执行多段代码,给我们感觉上好像这些代码
     * 在"同时运行"。
     * 
     * 创建线程有两种方式:
     * 方式一:继承Thread并重写run方法。
     * @author ta
     *
     */
    public class ThreadDemo1 {
        public static void main(String[] args) {
            MyThread1 t1 = new MyThread1();
            MyThread2 t2 = new MyThread2();    
            /*
             * 启动线程要调用start方法,而不是直接调用线程
             * 的run方法。
             */
            t1.start();
            t2.start();
        }
    }
    /**
     * 第一种创建线程的方式存在两个不足之处
     * 1:由于java是单继承的,这导致继承了Thread后就不能再
     *   继承其他类了。在实际开发中经常会继承某个超类来复
     *   用其中的方法,这导致两者不能同时继承。
     *   
     * 2:继承线程后重写run方法来定义任务,这又导致我们将任
     *   务直接定义在线程上,使得线程只能做该任务,无法并发
     *   执行其他任务,重用性变差。  
     * @author ta
     *
     */
    class MyThread1 extends Thread{
        public void run() {
            for(int i=0;i<1000;i++) {
                System.out.println("你是谁啊?");
            }
        }
    }
    class MyThread2 extends Thread{
        public void run() {
            for(int i=0;i<1000;i++) {
                System.out.println("我是查水表的!");
            }
        }
    }
    package thread;
    /**
     * 第二种从创建线程的方式:
     * 实现Runnable接口,单独定义线程任务
     * @author ta
     *
     */
    public class ThreadDemo2 {
        public static void main(String[] args) {
            //实例化任务
            Runnable r1 = new MyRunnable1();
            Runnable r2 = new MyRunnable2();
            
            //创建线程
            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r2);
            
            t1.start();
            t2.start();
            
        }
    }
    
    class MyRunnable1 implements Runnable{
        public void run() {
            for(int i=0;i<1000;i++) {
                System.out.println("你是谁啊?");
            }
        }
    }
    class MyRunnable2 implements Runnable{
        public void run() {
            for(int i=0;i<1000;i++) {
                System.out.println("我是查水表的!");
            }
        }
    }

    线程提供了一系列获取当前线程信息的方法

    package thread;
    /**
     * 线程提供了一系列获取当前线程信息的方法
     * @author ta
     *
     */
    public class ThreadInfoDemo {
        public static void main(String[] args) {
            //获取主线程
            Thread main = Thread.currentThread();
            //获取线程的唯一标识
            long id = main.getId();
            System.out.println("id:"+id);
            
            //获取线程的名字
            String name = main.getName();
            System.out.println("name:"+name);        
            //获取优先级
            int priority = main.getPriority();
            System.out.println("priority:"+priority);        
            //显示是否处于活动状态
            boolean isAlive = main.isAlive();
            //显示是否为守护线程
            boolean isDaemon = main.isDaemon();
            //显示线程是否被中断
            boolean isInterrupted = main.isInterrupted();
            System.out.println("是否活着:"+isAlive);
            System.out.println("是否为守护线程:"+isDaemon);
            System.out.println("是否被中断:"+isInterrupted);
            
            
            
            
        }
    }

    如何创建线程池

    package thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 线程池
     * 线程池是一个管理线程的机制。它主要解决两个问题:
     * 1:重用线程
     * 2:控制线程数量
     * 频繁的创建和销毁线程会给系统带来额外的开销,所以线程应当
     * 得以重用。
     * 当线程数量过多时,会出现资源消耗增大,CPU出现过度切换导致
     * 并发性能降低。对此线程的数量也要得以控制在当前硬件环境所能
     * 承受的范围内。
     * 
     * @author ta
     *
     */
    public class ThreadPoolDemo {
        public static void main(String[] args) {
            ExecutorService threadPool 
                = Executors.newFixedThreadPool(2);
            
            for(int i=0;i<5;i++){
                Runnable r = new Runnable() {
                    public void run() {
                        Thread t = Thread.currentThread();
                        System.out.println(t.getName()+":正在执行任务...");
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                        }
                        System.out.println(t.getName()+":执行任务完毕...");
                    }
                };
                //将任务交给线程池
                threadPool.execute(r);
                System.out.println("将一个任务指派给了线程池!");
            }
            
            
            
    //        threadPool.shutdown();//不是立即关闭,只是不再接受新任务
    //        threadPool.shutdownNow();//立刻停止,任务没执行完都停
    //        System.out.println("线程池停止了!");
        }
    }

    关于线程安全问题

    package thread;
    /**
     * 多线程并发的安全问题
     * 当多个线程并发操作同一资源时,由于线程切换时机的不确定
     * 和不可控,会导致操作该资源的代码逻辑执行顺序未按照设计
     * 要求运行,出现了操作混乱。严重时可能导致系统瘫痪。
     * @author ta
     *
     */
    public class SyncDemo {
        public static void main(String[] args) {
            final Table table = new Table();
            Thread t1 = new Thread() {
                public void run() {
                    while(true) {
                        int bean = table.getBean();
                        Thread.yield();
                        System.out.println(getName()+":"+bean);
                    }
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    while(true) {
                        int bean = table.getBean();
                        Thread.yield();
                        System.out.println(getName()+":"+bean);
                    }
                }
            };
            t1.start();
            t2.start();
        }
    }
    
    class Table{
        //桌子上有20个豆子
        private int beans = 20;
        /**
         * 解决并发安全问题的核心就是将多个线程抢着运行改为
         * 有先后顺序的排队运行。
         * Java提供了锁机制,强制多个线程同步运行一个方法
         * 
         * 当一个方法上使用关键字:synchronized修饰后,该方法
         * 称为同步方法,多个线程不能同时在方法内部运行。
         * @return
         */
        public synchronized int getBean() {
            if(beans==0) {
                throw new RuntimeException("没有豆子了!");
            }
            /*
             * yield方法会让运行这个方法的线程立刻让出CPU时间
             * 在这里是为了模拟发生线程切换。
             */
            Thread.yield();
            return beans--;
        }
    }
    package thread;
    /**
     * 同步块
     * 有效的缩小同步范围可以在保证并发安全的前提下尽可能
     * 的提高并发效率。
     * 
     * 语法:
     * synchronized(同步监视器对象){
     *     需用同步运行的代码片段
     * }
     * @author ta
     *
     */
    public class SyncDemo2 {
        public static void main(String[] args) {
            final Shop shop = new Shop();
            Thread t1 = new Thread() {
                public void run() {
                    shop.buy();
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    shop.buy();
                }
            };
            
            t1.start();
            t2.start();
        }
    }
    
    class Shop{
        
        /**
         * 直接在方法上使用synchroinzed,那么同步监视器
         * 对象就是当前方法所属对象,即:方法中看到的this
         */
    //    public synchronized void buy() {
        public void buy() {
            try {
                Thread t = Thread.currentThread();
                System.out.println(t.getName()+":正在挑衣服...");
                Thread.sleep(5000);
                /*
                 * 同步块有一个要求,多个线程看到的同步监视器
                 * 对象必须是同一个!否则没有同步运行效果。
                 * 具体使用哪个对象可以结合将来实际开发需求而定。
                 */
                synchronized (this) {
                    System.out.println(t.getName()+":正在试衣服...");
                    Thread.sleep(5000);
                }
                
                
                System.out.println(t.getName()+":结账离开");
                
            } catch (Exception e) {
            }
        }
    }
    package thread;
    /**
     * 静态方法若使用synchronized修饰后,那么一定具有同步
     * 效果。
     * @author ta
     *
     */
    public class SyncDemo3 {
        public static void main(String[] args) {
            Thread t1 = new Thread() {
                public void run() {
                    Boo.dosome();
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    Boo.dosome();
                }
            };
            t1.start();
            t2.start();
        }
    }
    
    class Boo{
        public synchronized static void dosome() {
            try {
                Thread t = Thread.currentThread();
                System.out.println(t.getName()+":正在运行dosome方法");
                Thread.sleep(5000);
                System.out.println(t.getName()+":运行dosome方法完毕");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    互斥锁

    package thread;
    /**
     * 互斥锁
     * 当synchronized同时锁定多段代码片段时,并且他们指定
     * 的同步监视器对象是[同一个]时,那么这些代码片段之间就
     * 是互斥的,即:多个线程不能同时执行这些代码片段。
     * @author ta
     *
     */
    public class SyncDemo4 {
        public static void main(String[] args) {
            final Foo foo = new Foo();
            Thread t1 = new Thread() {
                public void run() {
                    foo.methodA();
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    foo.methodB();
                }
            };
            t1.start();
            t2.start();
        }
    }
    
    class Foo{
        public synchronized void methodA() {
            try {
                Thread t = Thread.currentThread();
                System.out.println(t.getName()+":正在执行A方法...");
                Thread.sleep(5000);
                System.out.println(t.getName()+":执行A方法完毕");
            } catch (Exception e) {
            }
        }
        public void methodB() {
            synchronized (this) {
                try {
                    Thread t = Thread.currentThread();
                    System.out.println(t.getName()+":正在执行B方法...");
                    Thread.sleep(5000);
                    System.out.println(t.getName()+":执行B方法完毕");
                } catch (Exception e) {
                }
            }
        }
    }

     睡眠阻塞

    package thread;
    
    import java.util.Scanner;
    
    /**
     * 睡眠阻塞
     * 
     * static void sleep(long ms)
     * 该方法会让运行这个方法的线程处于阻塞状态指定的毫秒,
     * 当超时后,线程会自动回到RUNNABLE状态,等待再次获取
     * 时间片并发运行。
     * 
     * 注:一个线程进入阻塞状态时,CPU会立刻释放去并发执行
     * 其他线程,直到该线程接触阻塞状态为止。
     * @author ta
     *
     */
    public class SleepDemo {
        public static void main(String[] args) {
            /*
             * 程序启动后,输入一个数字,如:100
             * 然后每一秒钟递减一次并输出,到0时停止。
             */
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个数字:");
            int num = Integer.parseInt(scanner.nextLine());
            for(int i=num;i>0;i--) {
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("结束.");
        }
    }
    package thread;
    /**
     * 当一个线程调用sleep方法处于阻塞状态时,其他线程调用
     * 该线程的中断方法时,那么该线程的sleep方法会立即抛出
     * 中断异常并打断睡眠阻塞。
     * @author ta
     *
     */
    public class SleepDemo2 {
        public static void main(String[] args) {
            /*
             * JDK8之前,有一个要求,即:
             * 当一个方法中的局部内部类想引用这个方法的其他
             * 局部变量,那么这个变量必须是final的。
             * 好比main方法中的局部内部类huang中的run方法里
             * 引用了main方法的局部变量lin,那么这个变量就
             * 必须声明为final的。
             */
            final Thread lin = new Thread() {
                public void run() {
                    System.out.println("林:刚美完容,睡一会.");
                    try {
                        /*
                         * 当一个线程调用sleep阻塞时,被其他线程
                         * 中断时会抛出中断异常
                         */
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        System.out.println("林:干嘛呢!干嘛呢!干嘛呢!都破了相了!");
                    }
                    System.out.println("林:醒了");
                }
            };
            
            Thread huang = new Thread() {
                public void run() {
                    System.out.println("黄:开始砸墙!");
                    for(int i=0;i<5;i++) {
                        System.out.println("黄:80!");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                    System.out.println("咣当!");
                    System.out.println("黄:搞定!");
                    //中断lin线程
                    lin.interrupt();
                }
            };
            
            lin.start();
            huang.start();
        }
    }

     线程优先级

    package thread;
    /**
     * 线程优先级
     * 
     * 线程无法主动获取CPU时间片,唯一可以干涉线程调度工作
     * 的方式就是修改线程优先级,最大程度的改善获取CPU时间
     * 片的几率。
     * 理论上,线程优先级越高的线程获取CPU时间片的次数越多
     * 
     * 线程有10个优先级,分别用整数1-10表示。
     * @author ta
     *
     */
    public class PriorityDemo {
        public static void main(String[] args) {
            Thread max = new Thread() {
                public void run() {
                    for(int i=0;i<10000;i++) {
                        System.out.println("max");
                    }
                }
            };
            Thread min = new Thread() {
                public void run() {
                    for(int i=0;i<10000;i++) {
                        System.out.println("min");
                    }
                }
            };
            Thread norm = new Thread() {
                public void run() {
                    for(int i=0;i<10000;i++) {
                        System.out.println("nor");
                    }
                }
            };
    
            max.setPriority(Thread.MAX_PRIORITY);
            min.setPriority(Thread.MIN_PRIORITY);
            
            min.start();
            norm.start();
            max.start();
            
        }
    }

    join方法

    package thread;
    /**
     * join方法可以协调线程之间的同步运行
     * 
     * 异步运行:代码之间运行没有先后顺序,各干各的。
     * 同步运行:代码运行有先后顺序。
     * @author ta
     *
     */
    public class JoinDemo {
        //标示图片是否下载完毕
        public static boolean isFinish = false;
        
        public static void main(String[] args) {
            final Thread download = new Thread() {
                public void run() {
                    System.out.println("down:开始下载图片...");
                    for(int i=1;i<=100;i++) {
                        System.out.println("down:已下载"+i+"%");
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                        }
                    }
                    System.out.println("down:图片下载完毕!");
                    isFinish = true;
                }
            };        
            
            Thread show = new Thread() {
                public void run() {
                    System.out.println("show:开始显示图片...");
                    /*
                     * 先等在下载线程将图片下载完毕
                     */
                    try {
                        /*
                         * 当show线程调用download线程的join方法后就
                         * 进入了阻塞状态,直到download线程结束才会
                         * 解除阻塞。
                         */
                        download.join();
                    } catch (InterruptedException e) {
                    }
                    
                    if(!isFinish) {
                        throw new RuntimeException("图片加载失败!");
                    }
                    System.out.println("show:显示图片完毕!");
                }
            };
            
            download.start();
            show.start();
            
            
            
        }
    }

    守护线程

    package thread;
    /**
     * 守护线程
     * 
     * 守护线程又称为后台线程,默认创建出来的线程都是普通
     * 线程或称为前台线程。只有调用线程的setDaemon方法后
     * 才会将该线程设置为守护线程。
     * 
     * 守护线程使用上与前台线程一样,但是在结束时机上有一点
     * 不同:当进程结束时,所有正在运行的守护线程都会被强制
     * 中断。
     * 进程的结束:当一个进程中没有任何前台线程时即结束。
     * 
     * @author ta
     *
     */
    public class DaemonThreadDemo {
        public static void main(String[] args) {
            
            Thread rose = new Thread() {
                public void run() {
                    for(int i=0;i<5;i++) {
                        System.out.println("rose:let me go!");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                    System.out.println("rose:啊啊啊啊啊AAAAAAaaaaa....");
                    System.out.println("噗通!");
                }
            };
            
            Thread jack = new Thread() {
                public void run() {
                    while(true) {
                        System.out.println("jack:you jump!i jump!");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            };
            
            rose.start();
            /*
             * 设置守护线程必须要在start方法之前进行。
             */
            jack.setDaemon(true);
            jack.start();
            
            
            
        }
    }

    static Thread currentThread()

    package thread;
    /**
     * Thread提供了一个静态方法:
     * static Thread currentThread()
     * 该方法可以获取运行这个方法的线程。
     * 
     * 后期常用的一个API: ThreadLocal里面就会用到这个方法
     * 来实现功能。
     * @author ta
     *
     */
    public class CurrentThreadDemo {
        public static void main(String[] args) {
            Thread main = Thread.currentThread();
            System.out.println("运行main方法的线程是:"+main);
            
            Thread t = new Thread() {
                public void run() {
                    Thread t = Thread.currentThread();
                    System.out.println("自定义线程:"+t);
                    dosome();
                }
            };
            t.start();
            
            dosome();
        }
        
        public static void dosome() {
            Thread t = Thread.currentThread();
            System.out.println("运行dosome方法的线程是:"+t);
        }
    }
  • 相关阅读:
    java 数据结构(六):数组与集合
    java 数据结构(七):Collection接口
    java 数据结构(八):Iterator接口与foreach循环
    java 数据结构(九):Collection子接口:List接口
    java 数据结构(十):Collection子接口:Set接口
    java 数据结构(十一):Map接口
    java 数据结构(十二):Collections工具类的使用
    java 面向对象(三十二):泛型一 泛型的理解
    java 面向对象(三十三):泛型二 泛型在集合中的使用
    iOS下JS与OC互相调用(四)--JavaScriptCore
  • 原文地址:https://www.cnblogs.com/package-java/p/10419380.html
Copyright © 2011-2022 走看看