zoukankan      html  css  js  c++  java
  • 线程池,多线程,线程异步,同步和死锁,Lock接口

     线程池

      线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

    除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。线程池主要用来解决线程生命周期开销问题和资源不足问题。

     使用线程池方式--Runnable接口

    通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。

    Executors:线程池创建工厂类
        public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
    ExecutorService:线程池类
        Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
    Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

    使用线程池中线程对象的步骤:

    1.  创建线程池对象

    2.  创建Runnable接口子类对象

    3.  提交Runnable接口子类对象

    4.  关闭线程池

    package com.oracle.xiancheng;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Xiancheng {
        public static void main(String[] args) {
            //创建线程池对象(从线程池工厂中获得)
            ExecutorService ec=Executors.newFixedThreadPool(2);
            //创建Runnable接口子类实例对象(创建任务对象)
            MyRunnable r = new MyRunnable();
            //    提交Runnable接口子类对象
            //线程池会选一条线程执行提交的任务
            ec.submit(r);
            //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
            //又将使用完的线程又归还到了线程池中
            for(int i=0;i<50;i++){
                System.out.println("main..."+i);
            }
            ec.shutdown();//关闭线程池
        }
    }

    Runnable接口实现类

    package com.oracle.xiancheng;
    public class MyRunnable  implements Runnable {
    
        public void run() {
            for(int i=0;i<50;i++){
                System.out.println("runable..."+i);
            }
        }
    }

    使用线程池方式—Callable接口

    Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
    ExecutorService:线程池类
        <T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
    Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

      使用线程池中线程对象的步骤:

      1. 创建线程池对象

      2. 创建Callable接口子类对象

      3. 提交Callable接口子类对象

      4.  关闭线程池

    Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果

    package com.oracle.xiancheng;
    
    import java.util.concurrent.Callable;
    
    //Callable泛型就是你call的返回值类型
    public class MyCallable implements Callable<String> {
        public String call() throws Exception {
            
            return "这是call方法";
        }
    
    }

    测试类:

    package com.oracle.xiancheng;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class demo01 {
    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            //获取线程池
            ExecutorService ec=Executors.newFixedThreadPool(2);
            //创建callable子类
            MyCallable mc=new MyCallable();
            //提交Callable子类
            Future<String> f=ec.submit(mc);
            String str=f.get();
            System.out.println(str);
            //关闭线程池
            ec.shutdown();
        }
    
    }

    异步计算0-100的和跟0-200的和

    package com.oracle.xiancheng;
    
    import java.util.concurrent.Callable;
    
    public class JiSuan  implements Callable<Integer>{
        private Integer a;
        public JiSuan(Integer a){
            this.a=a;
        }
        public Integer call(){
            int sum=0;
            for(int i=0;i<=a;i++){
                sum +=i;
            }
            System.out.println(sum);
            return sum;
        }
        
    }

    测试类:

    package com.oracle.xiancheng;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Yibu {
        //异步计算0-100的和跟0-200的和
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            //获取线程池
    
            ExecutorService ec=Executors.newFixedThreadPool(2);
            //创建callable子类
            JiSuan js=new JiSuan(100);
            JiSuan js1=new JiSuan(200);
            //提交callable子类
            Future<Integer> f1=ec.submit(js);
            Future<Integer> f2=ec.submit(js1);
            /*//从future中获取返回值
            System.out.println(f1.get());
            System.out.println(f2.get());*/
            //关闭
            ec.shutdown();
        }
        
        
    }

     多线程

     线程安全

      电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “功夫熊猫3”,本次电影的座位共100个(本场电影只能卖100张票)。

    我们来模拟电影院的售票窗口,实现多个窗口同时卖 “功夫熊猫3”这场电影票(多个窗口一起卖这100张票)

    需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟

    模拟票:

    public class Ticket implements Runnable {
        private int tickets=100;
       public void run(){
        //模拟卖票
    while(true){ if(tickets>0){
              //模拟选座
    try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"张票"); } } } }

    测试类;

    package com.oracle.demo01;
    
    public class demo01 {
        public static void main(String[] args) {
            Ticket t=new Ticket();//创建票对象
            Thread t1=new Thread(t);
            Thread t2=new Thread(t);
            Thread t3=new Thread(t);
            t1.start();
            t2.start();
            t3.start();
            
        }
    }

    结果会出现重复的票,还有错误的票,0和-1问题

     线程同步(线程安全处理Synchronized)

    java中提供了线程同步机制,它能够解决上述的线程安全问题。

             线程同步的方式有两种:

     方式1:同步代码块

     方式2:同步方法

     同步代码块

    //同步代码块: 在代码块声明上 加上synchronized
    synchronized (锁对象) {
        //可能会产生线程安全问题的代码
    }

      同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

    使用同步代码块,对电影院卖票案例中Ticket类进行如下代码修改:

    模拟出票

    public class Ticket implements Runnable {
        private int tickets=100;
        private Object lock = new Object();
        public void run(){
            while(true){
                
                synchronized (lock) {
                    if(tickets>0){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        
                        System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"张票");
                    }else{
                        break;
                    }
                }
    
                
            }
        }

     同步方法

    //同步方法:在方法声明上加上synchronized
    public synchronized void method(){
           //可能会产生线程安全问题的代码
    }

    同步方法中的锁对象是 this

    使用同步方法,对电影院卖票案例中Ticket类进行如下代码修改:

    package com.oracle.demo01;
    
    public class Ticket implements Runnable {
        private int tickets=100;
        private Object lock = new Object();
        
        public void run() {
            //模拟卖票
            while(true){
                //同步方法
                method();
            }
        }
        
        //同步方法,锁对象this
         public synchronized void method(){
                if (tickets> 0) {
                    //模拟选坐的操作
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"张票");
                }
          }
        
    
    
    }    
    //静态同步方法: 在方法声明上加上static synchronized
    public static synchronized void method(){
        //可能会产生线程安全问题的代码
    }

     静态同步方法中的锁对象是本类字节码对象  类名.class

    同步锁:对象锁,对象监视器

    同步是怎么保证安全性?

     因为同步中有锁,没有锁的线程,不能执行,只能等。

    StringBuilder比StringBuffer快

    原因:StringBuffer有同步关键字synchronized,安全性比StringBuilder高,但速度慢

      死锁

      同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。

    格式如:

    synchronzied(A锁){
        synchronized(B锁){
             
      }
    }

    定义锁对象类:

    package com.oracle.demo01;
    
    public class LockA {
    public final static LockA locka=new LockA();
    private LockA(){
        
    }
    }
    package com.oracle.demo01;
    
    public class LockB {
        public final static LockB lockb=new LockB();
        private LockB(){
            
        }
    }

    线程任务类:

    package com.oracle.demo01;
    
    public class DeadLock implements Runnable {
        private int i=0;
        
        public void run() {
            
            while(true){
                if(i%2==0){
                    //情况一
                    synchronized (LockA.locka){
                        System.out.println("IF......lockA");
                        synchronized (LockB.lockb){
                            System.out.println("IF......lockB");
                        }
                    }
                }else{
                    //情况二
                    synchronized (LockB.lockb){
                        System.out.println("else......lockB");
                        synchronized (LockA.locka){
                            System.out.println("else......lockA");
                        }
                    }
                }
                i++;
            }
        }
        
    }

    测试类:

    package com.oracle.demo01;
    
    public class TestDEADlock {
    
        public static void main(String[] args) {
            //创建线程任务类对象
            DeadLock dl=new DeadLock();
                //创建两个线程
            Thread t1=new Thread(dl);
            Thread t2=new Thread(dl);
               //开启线程
            t1.start();
            t2.start();
            
        }
    
    }

     Lock接口

      使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Ticket类进行如下代码修改:

    package com.oracle.demo01;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class NewTicket implements Runnable{
        private int tickets=100;
        //创建Lock锁对象
        private    Lock ck = new ReentrantLock();
            public void run(){
                while(true){
                    ck.lock();
                    if(tickets>0){
                        try {
                            Thread.sleep(10);
                            System.out.println(Thread.currentThread().getName()+"出售第"+tickets--+"张票");
        
                        } catch (InterruptedException e) {
                        
                            e.printStackTrace();
                        }finally{
                            ck.unlock();//释放锁
                        }
                    
                    }
                }
            }
    
    }
  • 相关阅读:
    查看端口有没有被占用
    微信公众号2()
    How to insert a segment of noise to music file
    puppet practice
    Docker Commands
    LempelZiv algorithm realization
    The algorithm of entropy realization
    Java network programmingguessing game
    Deploy Openstack with RDO and Change VNC console to Spice
    puppet overview
  • 原文地址:https://www.cnblogs.com/lzw123-/p/9545658.html
Copyright © 2011-2022 走看看