多线程丶线程同步丶线程池

版权声明:如需转载或引用请声明原文网址 https://blog.csdn.net/u013087359/article/details/81414277

一.多线程的实现方式

1.继承Thread类

    //1.继承Thread类
    class MyThread extends Thread{
        //2.重写run方法
        public void run() {
            System.out.println("继承Thread类开启多线程");
        }
    }

    //3.创建自定义的线程类对象
    MyThread mt=new MyThread();
    //4.调用start()方法,jvm会开启线程执行run()方法
    mt.start();

    //简化
    new Thread(){           
        public void run() {
            System.out.println("使用匿名类简化---继承Thread类");
        }
    }.start();

2.实现Runnable接口

    //1.实现Runnable接口
    class MyRunable implements Runnable{
        //2.重写run()方法
        public void run() {
            System.out.println("实现Runnable接口开启多线程");                
        }           
    }

    //3.创建Thread对象,并把Runnable实现类传递进构造参数
    MyRunable mr=new MyRunable();
    Thread t=new Thread(mr);

    //4.调用start()方法,jvm会开启线程执行run()方法
    t.start();

    //简化
    new Thread(new Runnable() {
        public void run() {
            System.out.println("使用匿名类简化---实现Runnable接口");               
        }
    }).start();

3.两种方式区别

  • 继承Thread类可以使用Thread类的方法,代码简便,但java只支持单继承,所以如果类已经有父类就不能再继承Thread类

  • 实现Runnable接口可以解决父类问题,但代码稍复杂,且不能直接调用Thread类方法

二.线程信息

1.线程名称

    //通过构造方法设置线程名称
    new Thread("线程一"){          
        public void run() {
            //使用父类方法设置线程名称
            super.setName("修改线程名");
            //获取线程名称
            System.out.println(this.getName());
        }
    }.start();

2.当前线程对象

    new Thread(new Runnable() {
        public void run() {
            //使用Thread类静态方法currentThread()获取当前线程对象
            System.out.println(Thread.currentThread().getName());               
        }
    }).start();

三.线程状态

1.睡眠等待sleep

线程睡眠:当前线程等待指定时间后继续执行线程

    try {
        //当前线程睡眠1000毫秒后再继续执行
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

2.守护线程|后台线程daemon

守护线程:当其他非守护线程全部执行完毕后,守护线程会马上终止执行

    Thread t= new Thread(){
        public void run(){
            try {

                Thread.sleep(3000);
                //程序不会执行到这里,因为该线程被设置为守护线程,当其他非守护线程执行完毕,该线程会立刻被终止
                System.out.println(this.getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    //设置t为守护线程,setDaemon()必须先start()前调用
    t.setDaemon(true);
    t.start();

    //判断t线程是否为守护线程
    System.out.println(t.isDaemon());
    System.out.println("程序结束了");

    //输出结果:
    //          true
    //          程序结束了

    //main主线程终止了,不存在其他非守护线程,所以守护线程t立刻被终止

3.阻塞线程join

阻塞线程:子线程阻塞主线程,使主线程暂停执行,等子线程执行一段时间或执行完毕后再恢复执行主线程

    public static void main(String[] args) throws InterruptedException {
        //t线程在main线程中执行,所以main相当于主线程,t线程相当于子线程
        Thread t=new Thread(){          
            public void run() {
                for(int i=0;i<1000;i++){
                    System.out.println(getName()+"...."+i);
                }
            }
        };


        t.start();

        //阻塞main主线程暂停执行,等待子线程t执行10毫秒后,主线程和子线程再同时继续执行
        //当参数为0或不填写时,会等待子线程执行完毕后才会恢复主线程的执行
        t.join(10);
        //大概在中间输出这句话,如果没有t.join(10),该行内容会输出在第一行
        System.out.println("==============程序结束=================");
    }

4.线程优先级

线程优先级:优先级越高的线程CPU会优先执行

    class MyThread extends Thread{          
        public void run() {
            for(int i=0;i<1000;i++){
                System.out.println(getName()+"...."+i);
            }
        }
    }

    Thread t1=new MyThread();
    Thread t2=new MyThread();

    //设置线程优先级,范围是MIN_PRIORITY~MAX_PRIORITY,即1~10
    //优先级越高的线程,CPU会优先执行
    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(Thread.MIN_PRIORITY);

    t1.start();
    t2.start();

    //获取线程的优先级
    System.out.println(t1.getPriority());
    System.out.println(t2.getPriority());

5.礼让线程yield

礼让线程:暂停当前线程,尽可能让其他线程优先执行,该操作也可能无效

四.线程同步

当多个线程同时读写一个数据时可能会导致数据不一致,所以需要一个同步区域解决这个问题。同步区域在同一个时间只能被一个线程访问,当该线程访问完毕,其他线程才能访问同步区域。

#

同步锁:相同同步锁(对象引用地址一样,与对象属性值是否改变无关)的同步区域会相互排斥

1.同步代码块

    //局部内部类访问外部变量需要使用final修饰
    final Object obj=new Object();

    new Thread(){
        public void run() {
            //同步锁,锁对象为obj
            synchronized (obj) {
                try {
                    //延时3秒
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程一");
            }
        };
    }.start();


    new Thread(){
        public void run() {
            //同步锁,锁对象为obj
            synchronized (obj) {
                System.out.println("线程二");
            }
        };
    }.start();

    //输出结果:
    //          (等待3秒)
    //          线程一
    //          线程二

    //两个线程的同步代码块都是同样的锁对象,所以这2个方法同时只能被一个线程访问
    //因为线程一先执行,所以线程二需要等待线程一的同步代码块执行完毕,才能执行线程二的同步代码块

2.同步方法

  • 同步成员方法同步锁对象是本类实例this
  • 静态方法同步锁对象是字节码对象
class MyClass{
    //1.同步成员方法同步锁对象是本类实例this
    public synchronized void print1(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(1);
    }


    public synchronized void print2(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(2);
    }

    //2.静态方法同步锁对象是字节码对象MyClass.class
    public synchronized static void print3(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(3);
    }


    public synchronized static void print4(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(4);
    }

3.死锁

同步代码相互嵌套时,使用相同的锁对象,可能会出现死锁

    final Object obj1=new Object();
    final Object obj2=new Object();
    new Thread("线程一"){
        public void run() {             
            synchronized (obj1) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (obj2) {

                }
            }
        };
    }.start();

    new Thread("线程二"){
        public void run() { 
            synchronized (obj2) {
                synchronized (obj1) {

                }
            }
        };
    }.start();

    //死锁分析:
    //线程一进入obj1同步块,等待3秒
    //在线程一等待3秒期间线程二进入obj2同步块
    //线程二准备obj1同步块,但发现被线程一占用,所以等待
    //线程一等待3秒结束,准备进入obj2同步块,但发现被线程二占用,所以等待
    //线程一和线程二相互等待,程序无法向下执行,形成了死锁

五.线程通信

线程通信:当前线程唤醒其他线程的等待状态

1.Object方式

public class Test1 {
    public static void main(String[] args) {
        final MyClass mc = new MyClass();

        new Thread("线程一") {
            public void run() {
                try {
                    //延时100毫秒,为了让所有线程都开启成功后再执行print1方法
                    Thread.sleep(100);
                    mc.print1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

        new Thread("线程二") {
            public void run() {
                try {
                    mc.print2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

        new Thread("线程三") {
            public void run() {
                try {
                    mc.print3();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

        new Thread("线程四") {
            public void run() {
                try {
                    mc.print4();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

        //输出结果:
        //      我是方法2
        //      我是方法3
        //      我是方法4
        //      1
        //      2
        //      4
        //      3

        //分析:
        //      1.线程一延时了100毫秒,所以线程二丶三丶四会先输出语句
        //      2.线程二丶三丶四都执行了wait()进入被唤醒状态
        //      3.线程一开始输出1
        //      4.线程一调用notify()随机唤醒线程二丶三丶四中的一个线程
        //      5.本次运行结果唤醒的是线程二,所以继续执行wait()后面的代码,输出2
        //      6.线程二最后调用了notifyAll(),所以唤醒了所有为被唤醒的线程(线程三丶四)
        //      7.线程三丶四分别执行后面的代码,输出4,输出3
    }
}

class MyClass {
    /*
     * 执行流程概述:
     * 1.print1()执行完随机唤醒一个线程
     * 2.其他方法都会先wait()等待,当被唤醒后会执行notifyAll()把其他线程都唤醒
     * */

    public synchronized void print1() throws InterruptedException {
        System.out.println(1);
        //Object方法1:notify()
        //随机唤醒一个未被唤醒的线程
        this.notify();
    }

    public synchronized void print2() throws InterruptedException {
        System.out.println("我是方法2");

        //Object方法2:wait()
        //wait()线程立刻等待,到这行代码停止执行,并释放锁       
        this.wait();
        //当被唤醒后执行wait()后面的代码,不是重新执行print2()方法       
        System.out.println(2);

        //Object方法3:notifyAll()
        //唤醒其他所有在未被唤醒的线程
        this.notifyAll();

    }

    public synchronized void print3() throws InterruptedException {
        System.out.println("我是方法3");
        this.wait();
        System.out.println(3);
        this.notifyAll();
    }

    public synchronized void print4() throws InterruptedException {
        System.out.println("我是方法4");
        this.wait();
        System.out.println(4);
        this.notifyAll();
    }

}

2.ReentrantLock与Condition

public class Test3 {
    public static void main(String[] args) {
        final MyClass2 m = new MyClass2();
        new Thread() {
            public void run() {
                for (int i = 0; i < 3; i++) {
                    try {
                        m.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }.start();

        new Thread() {
            public void run() {
                for (int i = 0; i < 3; i++) {
                    try {
                        m.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }.start();

        new Thread() {
            public void run() {
                for (int i = 0; i < 3; i++) {
                    try {
                        m.print3();
                    } catch (InterruptedException e) {

                        e.printStackTrace();
                    }
                }
            };
        }.start();


        //输出结果:
        //      1
        //      2
        //      3
        //      1
        //      2
        //      3
        //      1
        //      2
        //      3

    }

}

class MyClass2 {
    private int flag = 1;
    //1.创建ReentrantLock对象
    private ReentrantLock r = new ReentrantLock();
    //2.从同一个锁中获取多个条件,不同条件用于区分不同线程,执行各自线程的等待和唤醒
    private Condition c1 = r.newCondition();
    private Condition c2 = r.newCondition();
    private Condition c3 = r.newCondition();

    public void print1() throws InterruptedException {
        //3.获取锁
        r.lock();
        // 使用try-finally保证锁能被释放
        try {
            if (flag != 1) {
                //4.条件1的线程等待
                c1.await();
            }

            System.out.println(1);
            flag = 2;
            //5.唤醒条件2的线程
            c2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放锁
            r.unlock();
        }
    }

    public void print2() throws InterruptedException {
        r.lock();
        try {

            if (flag != 2) {
                c2.await();
            }

            System.out.println(2);
            flag = 3;
            c3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            r.unlock();
        }
    }

    public void print3() throws InterruptedException {
        r.lock();
        try {
            if (flag != 3) {
                c3.await();
            }

            System.out.println(3);
            flag = 1;
            c1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            r.unlock();
        }
    }
}

六.线程组

线程组:把线程进行分组,方便批量操作

    //1.创建线程组
    ThreadGroup tgA=new ThreadGroup("A组");
    ThreadGroup tgB=new ThreadGroup("B组");

    //2.在创建线程时指定所属线程组
    Thread t1=new Thread(tgA,"线程一"){            
        public void run() {         

            System.out.println(currentThread().getThreadGroup().getName()+"----"+ getName());
        }
    };

    Thread t2=new Thread(tgA,"线程二"){            
        public void run() {
            System.out.println(currentThread().getThreadGroup().getName()+"----"+ getName());
        }
    };

    class MyRunnable implements Runnable{
        public void run() {
            //获取当前线程对象
            Thread t=Thread.currentThread();
            //3.根据线程对象获取所属线程组对象
            ThreadGroup tg=t.getThreadGroup();
            //4.获取线程组名
            String groupName=tg.getName();
            System.out.println(groupName+"----"+ t.getName());
        }           
    }

    //5.在创建线程时指定所属线程组
    Thread t3=new Thread(tgB,new MyRunnable(),"线程三");
    Thread t4=new Thread(tgB,new MyRunnable(),"线程四");

    t1.start();
    t2.start();
    t3.start();
    t4.start();

    //6.获取线程组最高优先级
    System.out.println(tgA.getMaxPriority());
    //7.判断此线程组是否守护线程组
    System.out.println(tgB.isDaemon());

七.线程池

线程池是存放多个线程的容器,当需要使用线程时直接从线程池中获取,当线程执行完毕不会被销毁而是放回线程池中,这样避免了大量重复创建销毁线程的消耗,提高性能

1.Runnable

    Runnable r1=new Runnable(){
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"-a");
        }           
    };

    Runnable r2=new Runnable(){
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-b");
        }           
    };

    Runnable r3=new Runnable(){
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-c");
        }           
    };

    //1.创建容量为2的线程池
    ExecutorService es= Executors.newFixedThreadPool(2);

    //2.提交三个任务
    es.submit(r1);
    es.submit(r2);
    es.submit(r3);

    //3.结束线程池
    es.shutdown();

    //输出结果:
    //      (等待一秒)
    //      pool-1-thread-1-a
    //      pool-1-thread-2-b
    //      (等待一秒)
    //      pool-1-thread-1-c

    //分析:
    //      1.线程池中只有2个线程,所以同时只能开启线程,执行2个任务
    //      2.线程a和b会先执行,线程c需要等待
    //      3.线程a和b执行完毕后,把两个线程放回线程池
    //      4.线程c从线程池中获取线程执行

2.Callable

    //1.Callable相对于Runnable线程任务可以有返回值
    Callable<Integer> c1=new Callable<Integer>() {
        public Integer call() throws Exception {
            Integer sum=0;
            for(int i=0;i<100;i++){
                sum+=i;
            }
            return sum;
        }
    };

    Callable<String> c2=new Callable<String>() {
        public String call() throws Exception {
            Thread.sleep(3000);
            return "abc";
        }
    };


    //2.创建单个线程的线程池
    ExecutorService es= Executors.newSingleThreadExecutor();

    //3.提交Callable任务有返回值
    Future<Integer> f1=  es.submit(c1);
    Future<String> f2=es.submit(c2); 

    Thread.sleep(1000);

    //4.判断任务是否已完成
    if(f1.isDone()){
        //5.获取任务的返回值
        System.out.println("任务一返回结果:"+f1.get());
    }

    //6.尝试取消任务,需要在任务执行完成之前取消,否则会返回false
    f2.cancel(true);

    //7.判断任务是否被取消
    if(f2.isCancelled()){
        System.out.println("任务二已取消");
    }

    System.out.println(f2.isCancelled());

    //结束线程池
    es.shutdown();

八.定时器Timer

    //1.定时器任务
    TimerTask tt=new TimerTask() {          
        public void run() {
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));                
        }
    };

    //设置日期
    Calendar c=Calendar.getInstance();
    c.set(2018,7,5,13,24,15);
    Date d=c.getTime();

    //2.定时器
    Timer t=new Timer();

    //3.在指定日期定时执行任务
    //  第二个参数是首次执行任务的时间
    //      如果当前时间小于首次时间,则等待达到了首次执行开始执行任务
    //      如果当前时间大于首次时间,会马上就执行任务   
    //  第三个参数是任务重复执行的间隔,单位毫秒,不填写任务只执行一次
    t.schedule(tt, d);

    Thread.sleep(1000*30);

    //4.取消定时器
    t.cancel();

猜你喜欢

转载自blog.csdn.net/u013087359/article/details/81414277