zoukankan      html  css  js  c++  java
  • Java多线程、线程池和线程安全整理

        多线程

    1.1      多线程介绍

    进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

     

    1.2      Thread类

    通过API中搜索,查到Thread类。通过阅读Thread类中的描述。Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

    l  构造方法

     

    l  常用方法

    发现创建新执行线程有两种方法。

        一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。

        另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。

     

    创建线程的步骤:

    1 定义一个类继承Thread。

    2 重写run方法。

    3 创建子类对象,就是创建线程对象。

    4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

     

    package com.oracle.xiancheng;

     

    public class Demo01 extends Thread {

        public static void main(String[] args) {

            //创建线程

            MyThread mt=new MyThread();

            //创建线程

            mt.start();

            //获取正在执行的对象名称  调用 getname

           

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

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

                }

        }

    }

     

    自定义线程类

     

    package com.oracle.Runnable;

     

    public class MyRunnable implements Runnable{

     

        @Override

        public void run() {

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

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

           }

          

        }

       

    }

     

    1.2.1    实现Runnable的原理

    实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。

    创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。

    1.2.2    实现Runnable的好处

    第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

    1.3      线程的匿名内部类使用

    package com.oracle.Runnable;

     

    public class MyRunnable implements Runnable{

     

        @Override

        public void run() {

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

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

           }

          

        }

       

    }

    package com.oracle.Runnable;

     

    public class Demo02 {

        public static void main(String[] args) {

          

           //创建线程子类对象

           //匿名内部类对象

           //创建线程对象时,直接重写Thread类中的run方法

           Thread th=new Thread(){

               public void run() {

                  System.out.println(Thread.currentThread().getName()+"run");

               };

           };

           //开启线程

           th.start();

          

           //使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法

           /*Runnable r=new Runnable(){

               public void run() {

                  System.out.println(Thread.currentThread().getName()+"run");

               };

           };

           //创建线程任务对象

           Thread th=new Thread(r);

           //开启线程

           th.start();*/

          

        }

    }

    运行结果:

     

        线程池

    2.1      线程池概念

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

     

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

    l  Executors:线程池创建工厂类

    public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象

    l  ExecutorService:线程池类

    Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行

    l  Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

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

    创建线程池对象

    创建Runnable接口子类对象

    提交Runnable接口子类对象

    关闭线程池

    package com.oracle.Runnable;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    public class Demo03 {

         public static void main(String[] args) {

             // Executors 线程池工厂类

             // ExecutorService 线程池工厂类

             // 获取线程池对象

             ExecutorService es = Executors.newFixedThreadPool(2);

             // 创建线程任务对象

             MyRunnable mr = new MyRunnable();

             // 执行线程任务

             es.submit(mr);

             es.submit(mr);

             es.submit(mr);

             //释放资源

             es.shutdown();

            

         }

    }

    package com.oracle.Runnable;

     

    public class MyRunnable implements Runnable{

     

        @Override

        public void run() {

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

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

            }

           

        }

       

    }

    运行结果:

    等等

    2.3      使用线程池方式—Callable接口

    Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。

    ExecutorService:线程池类

    <T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法

    Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

     

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

    创建线程池对象

    创建Callable接口子类对象

    提交Callable接口子类对象

    关闭线程池

     

     

    package com.oracle.Runnable;

     

    import java.util.concurrent.Callable;

     

    public class MyCallable implements Callable<String> {

     

        @Override

        public String call() throws Exception {

           

            return "abc";

        }

       

    }

     

    package com.oracle.Runnable;

     

    import java.util.concurrent.ExecutionException;

    import java.util.concurrent.ExecutorCompletionService;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

     

    public class Demo04 {

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

            //创建线程任务

            MyCallable mc=new MyCallable();

            //获取线程池工厂

            ExecutorService es=Executors.newFixedThreadPool(2);

            Future<String> f=es.submit(mc);

            //创建返回值

            String  str=f.get();

            System.out.println(str);

        }

    }

     

    运行结果:

     

    2.4      线程池练习:返回两个数相加的结果和乘积的结果

     

    package com.oracle.Demo01;

     

    import java.util.concurrent.Callable;

     

    public class MyCallables implements Callable<Integer> {

        private int num1;

        private int num2;

        public MyCallables(int num1,int num2) {

            this.num1=num1;

            this.num2=num2;

        }

        @Override

        public Integer call() throws Exception {

           

            return num1+num2;

           

        }

     

    }

    package com.oracle.Demo01;

     

    import java.math.BigInteger;

    import java.util.concurrent.ExecutionException;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

     

    public class Test1 {

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

            //和

            MyCallables mc1=new MyCallables(100,150);

            MyCallables mc2=new MyCallables(10,15);

            ExecutorService es=Executors.newFixedThreadPool(2);

            Future<Integer> num1=es.submit(mc1);

            Future<Integer> num2=es.submit(mc2);

            int s1=num1.get();

            int  s2=num2.get();

            System.out.println(s1);

            System.out.println(s2);

            es.shutdown();

           

        }

    }

     

    运行结果:

     

     

     

     

    积:

     

    package com.oracle.Demo01;

     

    import java.math.BigDecimal;

    import java.math.BigInteger;

    import java.util.concurrent.Callable;

     

    public class MyCallablesr implements Callable<String > {

        private int num;

        public MyCallablesr(int num) {

            this.num=num;

           

        }

        @Override

        public String call() throws Exception {

            String  base="1";//超long的范围

            for(int i=1;i<=num;i++){

                //用BigDecimal转换

                BigDecimal stra=new BigDecimal(base);

                BigDecimal end=new BigDecimal(i);

                BigDecimal re=end.multiply(stra);

                base=re.toString();

               

            }

            return base ;

        }

     

    }

    package com.oracle.Demo01;

     

    import java.math.BigInteger;

    import java.util.concurrent.ExecutionException;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

     

    public class Test1 {

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

           

            //积

            MyCallablesr ms1=new  MyCallablesr(100);

            MyCallablesr ms2=new MyCallablesr(200);

            ExecutorService es=Executors.newFixedThreadPool(2);

            Future<String> base1=es.submit(ms1);

            Future<String> base2=es.submit(ms2);

            String s1=base1.get();

            String s2=base2.get();

            System.out.println(s1);

            System.out.println(s2);

            es.shutdown();

           

        }

    }

    运行结果:

     

     

        多线程

    3.1      线程安全

    如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    l  我们通过一个案例,演示线程的安全问题:

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

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

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

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

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

             线程同步的方式有两种:

    方式1:同步代码块

    方式2:同步方法

    3.2.1    同步代码块

    同步代码块: 在代码块声明上 加上synchronized

    synchronized (锁对象) {

        可能会产生线程安全问题的代码

    }

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

     

    模拟售票:

    package com.oracle.xianchengchi;

     

     

    public class MyRunnable implements Runnable {

        // 卖电影票

        private int ticket = 100;

        private Object obj = new Object();

     

        @Override

        public void run() {

            while (true) {

                synchronized (obj) {

                    if (ticket > 0) {

     

                        try {

                            Thread.sleep(50);

                        } catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                        }

                        System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");

     

                    }

     

                }

            }

     

        }

     

    }

     

    测试:

    package com.oracle.xianchengchi;

     

    public class Test01 {

        public static void main(String[] args) {

            //明确线程任务

            MyRunnable mr=new MyRunnable();

            Thread t0=new Thread(mr);

            Thread t1=new Thread(mr);

            Thread t2=new Thread(mr);

            //开启线程

            t0.start();

            t1.start();

            t2.start();

           

           

        }

    }

     

    运行结果:

     

    3.2.2    同步方法

    l  同步方法:在方法声明上加上synchronized

    public synchronized void method(){

       可能会产生线程安全问题的代码

    }

             同步方法中的锁对象是 this

            

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

    package com.oracle.xianchengchi;

     

    public class MyRunnables implements Runnable {

        // 卖电影票

        private int ticket = 100;

        private Object obj = new Object();

     

        @Override

        public void run() {

            while (true) {

               

                sale();

            }

           

     

        }

     

        public synchronized void sale() {

           

                if (ticket > 0) {

     

                    try {

                        Thread.sleep(50);

                    } catch (InterruptedException e) {

                        // TODO Auto-generated catch block

                        e.printStackTrace();

                    }

                    System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");

     

                }

     

            }

       

     

    }

    package com.oracle.xianchengchi;

     

    public class Test02 {

        public static void main(String[] args) {

            //明确线程任务

            MyRunnables mr=new MyRunnables();

            Thread t0=new Thread(mr);

            Thread t1=new Thread(mr);

            Thread t2=new Thread(mr);

            //开启线程

            t0.start();

            t1.start();

            t2.start();

           

           

        }

    }

    运行结果:

     

     

    3.3      Lock接口

    查阅API,查阅Lock接口描述,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

    l  Lock接口中的常用方法

    Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。

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

    package com.oracle.xianchengchi;

     

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

     

    public class MyRunnable2 implements Runnable {

        // 卖电影票

        private int ticket = 100;

        private Lock lock=new ReentrantLock();

        @Override

        public void run() {

            while (true) {

                lock.lock();

                    if (ticket > 0) {

     

                        try {

                            Thread.sleep(50);

                        } catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                        }

                        System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");

     

                    }

     

                lock.unlock();

            }

     

        }

  • 相关阅读:
    玩转车联网1---初识OBD和行车助手
    Confluence DotNet API发布
    深入理解最强桌面地图控件GMAP.NET ---[更新]百度地图
    猜想豌豆夹,360手机助手,腾讯手机管家,小米盒子传屏等工具开发思路
    有用文章搜藏
    Hbase Region Server整体架构
    无密码ssh操作步骤备忘
    cgwin的ssh错误解决办法
    Java系列笔记(1)
    SQL四种语言:DDL,DML,DCL,TCL
  • 原文地址:https://www.cnblogs.com/mengmengi/p/10652394.html
Copyright © 2011-2022 走看看