多线程

多线程

一、线程和进程的区别

  • 进程:进程是指开启一个应用程序,即一个服务。进程是一个具有一定独立功能的程序,在一个数据集上的一次动态执行过程

    是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。一般由程序、数据集合、进程控制块三部分组成。

  • 线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单元。一个进程可以有一个或者多个线程。

img

二、线程的一般方法

  1. 线程的创建

    • 继承Thread类
    • 实现Runnable接口

    方式一:继承Thread类

    public class testThread extends Thread{
        public class testThread(String name){
            super(name);
        }
        public void run(){
            System.out,printf(getName()+"线程执行了");
        }
        public static void main(String[]agrs){
            Thread t1 = new testThread("李白");
            t1.start();
        }
    }
    

    方式二:实现Runnable接口

    public class testThread2 implements Runnable{
        private String name;
        public testThread2(String name){
            this.name = name;
        }
        @Override
        public void run(){
            System.out.printf(Thread.CurrentThread.getName()+"线程执行了");
        }
        public static void main(String[]args){
            testThread2 test = new testThread2("白居易");
            Thread t2 = new Thread(test,test.name);
            t2.start();
        }
    }
    

    还可以通过匿名类的方法来实现

    public class testThread3 {
        public static void main(String[]args){
            Thread t3 = new Thread(){
                public void run(){
                    System.out.printf(Thread.CurrentThread.getName()+"线程运行了");
                }
            };
            t3.start();
        }
        
        Thread t4 = new Thread(new Runnable{
            @Override
            public void run(){
                System.out.printf(Thread.CurrentThread.getName()+"线程执行了");
            }
            
        },"白居易");
        t4.start();
    }
    
    1. 线程中的一般方法
    * sleep() 睡眠方法
        执行此方法,使当前线程睡眠一段时间,用于减缓程序执行的速度
    * setPriority() 设置线程的优先级
        线程的执行一般是按照优先级的大小来执行。线程的优先级为一个1~10的整数,默认提供的优先级是5,最小的优先级为MinPriority = 1
        MaxPriority = 10;
    * yield() 对调度线程的一个暗示,即当前线程愿意放弃当前cup的资源让与之同级的线程能够执行,这是保证线程执行的最佳方案。
    * join()等待这个线程死亡 即两个线程A、B 如果 Ajoin(B) 那么会先执行B线程,当B线程执行完毕后A线程才会执行。
       
    

    三、线程的同步

    • 线程同步的方式

      1. 通过Object的wait和notify方式 配合 synchronized线程同步锁实现

        public static boolean flag = false;
        public static int num = 0;
        public static void main(String[]args){
            Man man = new Man();
            new Thread(() ->{
                man.getRunnable1();
            }).start();
            new Thread(() ->{
                man.getRunnable2();
            }).start();
        }
        
        
        public static class Man{
            public synchronized void getRunnable1(){
                for(int i = 0;i<20;i++){
                    while(flag){
                        try{
                            wait();
                        }catch(InterruptedException e){
                            
                            e.printStackTrace();
                        }
                    }
                    System.out.println("生产出:"+(++num)+"个");
                    flag = true;
                    norify();
                }
            }
            public synchronized void getRunnable2(){
                for(int i = 0;i<20;i++){
                    while(!flag){
                        try{
                            wait();
                        }catch(InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    //模拟加载时间
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println("取出:"+(num--)+"个");
                    System.out.println("---------------");
                    flag = false;
                    notify();
                }
            }
        }
        

        2、通过Condition的await和signal 配合 lock锁实现

        public static boolean flag = false;
        public static int num = 0;
        public static void main(String[]args){
            Man man = new Man();
            new Thread(() ->{
                man.getRunnable1();
            }).start();
            new Thread(() ->{
                man.getRunnable2();
            }).start();
        }
        
        
        public static class Man{
            public static ReentrantLock lock = new ReentrantLock();
            public static Condition condtion = lock.newCondition();
            
            public  void getRunnable1(){
                lock.lock();
                try{
                for(int i = 0;i<20;i++){
                    while(flag){
                        try{
                            condition.await();
                        }catch(InterruptedException e){
                            
                            e.printStackTrace();
                        }
                    }
                    System.out.println("生产出:"+(++num)+"个");
                    flag = true;
                    condition.signal();
                	}
                }finally{
                    lock.unlock();
                }
            }
            public synchronized void getRunnable2(){
                lock.lock();
                try{
                for(int i = 0;i<20;i++){
                    while(!flag){
                        try{
                            condition.await();
                        }catch(InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    //模拟加载时间
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println("取出:"+(num--)+"个");
                    System.out.println("---------------");
                    flag = false;
                    condition.signal();
                	}
                }finally{
                    lock.unlock();
                }
            }
        }
        

        3、通过阻塞队列实现

          public static class Man {
         
                ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(64);
         
                public void getRunnable1() {
                    for (int i = 0; i < 8; i++) {
                        System.out.println("生产出:" + i + "个");
                        try {
                            queue.put(i);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("---------------生产完毕-----------------");
                }
         
                public void getRunnable2() {
                    for (int i = 0; i < 8; i++) {
                        try {
                            int num = (int) queue.take();
                            System.out.println("取出出:" + num);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        很明显使用阻塞队列代码精炼了很多,在这还可以发现这个阻塞队列是具有缓存功能的,想很多Android中网络访问框架内部就是使用这个进行缓存的,例如Volley、Okhttp等等。
        
        2、通过两个阻塞队列
        使用一个阻塞队列能够实现线程同步的功能,两个阻塞队列也可以实现线程同步。原理是ArrayBlockingQueue他是具有容量的,如果把他的容量定位1则意味着他只能放进去一个元素,第二个方进行就会就会被阻塞。按照这个原理进行来实现,定义两个容量为1的阻塞队列ArrayBlockingQueue,一个存放数据,另一个用于控制次序。main方法和上面一致,主要来看看Man类中的两个方法:
        
            static class Man {
                //数据的存放
                ArrayBlockingQueue queue1 = new ArrayBlockingQueue<Integer>(1);
                //用于控制程序的执行
                ArrayBlockingQueue queue2 = new ArrayBlockingQueue<Integer>(1);
         
                {
                    try {
                        //queue2放进去一个元素,getRunnable2阻塞
                        queue2.put(22222);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
         
                public void getRunnable1() {
                    new Thread(() -> {
                        for (int j = 0; j < 20; j++) {
                            try {
                                //queue1放进一个元素,getRunnable1阻塞
         
                                queue1.put(j);
                                System.out.println("存放   线程名称:" + Thread.currentThread().getName() + "-数据为-" + j);
         
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
         
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
         
                            try {
                                //queue2取出元素,getRunnable2进入
                                queue2.take();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                }
         
                public void getRunnable2() {
                    new Thread(() -> {
                        for (int j = 0; j < 20; j++) {
                            try {
                                //queue2放进一个元素,getRunnable2阻塞
                                queue2.put(22222);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
         
                            try {
                                //queue1放进一个元素,getRunnable1进入
         
                                int i = (int) queue1.take();
                                System.out.println("获取   线程名称:" + Thread.currentThread().getName() + "-数据为-" + i);
         
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                }
            }
        再次提醒queue2用于控制程序的执行次序,并无实际含义。最后看看运行效果,存一个、取一个很清晰,如下:
        3、通过SynchronousQueue
        SynchronousQueue不同于一般的数据等线程,而是线程等待数据,他是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。通过这一特性来实现一个多线程同步问题的解决方案,代码如下:
        
            /**
             * 使用阻塞队列SynchronousQueue
             * offer将数据插入队尾
             * take取出数据,如果没有则阻塞,直到有数据在获取到
             */
            public static void test() {
                SynchronousQueue queue = new SynchronousQueue();
                ExecutorService executorService = Executors.newSingleThreadExecutor();
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                            queue.offer(9);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
                try {
                    int take = (int) queue.take();
                    System.out.println(take);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        子线程中进行设置数据,而主线程获取数据,如果子线程没执行完毕,子线程没有执行完毕主线程就会被阻塞住不能执行下一步。
        
        六、通过线程池的Callback回调
        在线程的创建中,有一种创建方法可以返回线程结果,就是callback,他能返回线程的执行结果,通过子线程返回的结果进而在主线程中进行操作,也是一种同步方法,这种同步在Android中特别适用,例如Android中的AsyncTask源码中任务的创建部分。代码如下:
        
            private static void test() {
                ExecutorService executorService = Executors.newFixedThreadPool(5);
                Future<Boolean> submit = executorService.submit(new Callable<Boolean>() {
                    @Override
                    public Boolean call() throws Exception {
                        return false;
                    }
                });
                try {
                    if (submit.get()) {
                        System.out.println(true);
                    } else {
                        System.out.println(false);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        4、通过同步辅助类CountDownLatch
        CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。他类实际上是使用计数器的方式去控制的,在创建的时候传入一个int数值每当我们调用countDownt()方法的时候就使得这个变量的值减1,而对于await()方法则去判断这个int的变量的值是否为0,是则表示所有的操作都已经完成,否则继续等待。可以理解成倒计时锁。
        
        public class Test7 {
            public static void main(String[] args) {
                //启动两个线程,分别执行完毕之后再执行主线程
                CountDownLatch countDownLatch = new CountDownLatch(2);
         
                //线程1执行
                Thread thread1 = new Thread(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "线程执行完毕");
                    countDownLatch.countDown();
                });
                //线程2执行
                Thread thread2 = new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + "线程执行完毕");
                    countDownLatch.countDown();
                });
         
         
                thread1.start();
                thread2.start();
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
         
                //执行主线程
                System.out.println("主线程执行完毕");
            }
        }
        
        5、通过同步辅助类CyclicBarrier
        CyclicBarrier是一个同步的辅助类,和上面的CountDownLatch比较类似,不同的是他允许一组线程相互之间等待,达到一个共同点,再继续执行。可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍。
        
        public class Test8 {
            public static void main(String[] args) {
                //启动两个线程,分别执行完毕之后再执行主线程
                CyclicBarrier barrier  = new CyclicBarrier(2, () -> {
                    //执行主线程
                    System.out.println("主线程执行完毕");
         
                });
         
                //线程1执行
                Thread thread1 = new Thread(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
         
                    System.out.println(Thread.currentThread().getName() + "线程执行完毕");
         
                    try {
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                });
         
                //线程2执行
                Thread thread2 = new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + "线程执行完毕");
                    try {
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                });
         
         
                thread1.start();
                thread2.start();
            }
        }
        
        
        
        
posted @ 2020-12-22 16:26  coder象  阅读(56)  评论(0)    收藏  举报