Java多线程的实现

Java多线程的实现java

用多线程只有一个目的:更好的利用cpu资源.烧水的例子.(当洗杯子花5分钟,线程要停5分钟等待返回结果才能进行后续的烧水操做,新开一个线程执行洗杯子操做)。web

1、关于线程的一些概念算法

  • cpu时间片:咱们操做系统看起来能够多个程序同时运行.分时操做系统,将时间分红长短相同的时间区域,分配给一个线程使用,当线程尚未结束,时间片已通过去,该线程只有先中止,等待下一个时间片.cpu运行很快,中间的停顿感受不出来.
  • 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程(好比,下载程序,开启多个线程同时进行.)
  • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
  • 并发:经过cpu调度算法,让用户看上去同时执行,实际上从cpu操做层面不是真正的同时。并发每每在场景中有公用的资源,那么针对这个公用的资源每每产生瓶颈,咱们会用TPS或者QPS来反应这个系统的处理能力.
  • 线程安全:常常用来描绘一段代码。指在并发的状况之下,该代码通过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,咱们只须要关注系统的内存,cpu是否是够用便可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果.
  • Java中的同步指的是经过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提升性能,才是优秀的程序。线程安全的优先级高于性能.

2、Java多线程的实现安全

一、继承Thread类建立线程多线程

Thread类本质上是实现了Runnable接口,启动该线程的惟一方法是start()方法,并发

public class MyThread extends Thread{
    //普通的调用方法,定义任务要完成的工做.
    @Override
    public void run() {
        System.out.println("新线程正在执行,处理相关的逻辑!");
    }
}


public class Test {
    public static void main(String[] args) {
        //实例化对象
        MyThread myThread1 = new MyThread();  
        MyThread myThread2 = new MyThread();    
        //开启新的线程,分配新的资源
        myThread1.start();
        myThread2.start();
    }
}

二、实现Runnable接口建立线程框架

java中是单继承的,若是继承了一个类,就不能直接继承Thread类,须要实现Runnable接口的方式达到开启新线程的目的.异步

public class MyThread implements Runnable {
    //普通的调用方法,定义任务要完成的工做.
    @Override
    public void run() {
        System.out.println("新线程正在执行,处理相关的逻辑!");
    }    
 }
 
 
 public class Test {
    public static void main(String[] args) {
         MyThread myThread = new MyThread();  
        Thread thread =new Thread(myThread);
        //开启新的线程,分配新的资源
        thread.start();
    }
}

三、实现Callable接口ide

Callable接口的call()方法相似run()方法,都是定义任务要完成的工做.主要不一样点是call()方法是有返回值的、能够抛出异常。Callable类型的任务能够有两种方法开启执行.性能

方法一:借助FutureTask执行(FutureTask、Callable)

将Callable接口对象放到FutureTask对象中,FutureTask的get()方法,能够获取返回值.

public class MyCallableTask  implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("新线程正在执行,处理相关的逻辑!");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++) {
            sum += i;
        }
        return sum;
    }    
}

    
public class Test {
    public static void main(String[] args) {
        Callable<Integer> mycallabletask = new MyCallableTask();   
        //由Callable<Integer>建立一个FutureTask<Integer>对象:   
        FutureTask<Integer> futuretask = new FutureTask<Integer>(mycallabletask);   
        //注释:FutureTask<Integer>是一个包装器,它经过接受Callable<Integer>来建立,它同时实现了Future            和Runnable接口。 
        //由FutureTask<Integer>建立一个Thread对象:   
        Thread oneThread = new Thread(futuretask);
        oneThread.start();
        try {
            //经过futuretask中get()方法能够获得MyCallableTask的call()运行结果.
            //须要使用时获取出来,不然出现堵塞,本线程要等待新线程执行完返回结果才执行
            Integer i = futuretask.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

方法二:借助线程池来运行 (ExecutorService、Callable、Future)

ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。

执行Callable任务后,能够获取一个Future的对象,在该对象上调用get()就能够获取到Callable任务返回的Object了。

public class MyCallableTask  implements Callable<Integer> {    
    @Override
    public Integer call() throws Exception {
        System.out.println("新线程正在执行,处理相关的逻辑!");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++) {
            sum += i;
        }
        return sum;
    }
}

public class Test {
    public static void main(String[] args) {
        int taskSize = 5;
        //建立线程池
        ExecutorService threadPool = Executors.newCachedThreadPool(taskSize);
        //提交一个Callable任务,返回一个Future类型
        Future<Integer> future = threadPool.submit(new MyCallableTask());
         try {
            Thread.sleep(3000);//模拟本线程的一些任务
            //获取执行结果get方法是阻塞的
            System.out.println(future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }    
    }
}

采用匿名类直接新建Callable接口

public class Test{
    public static void main(String[] args) {
        // 建立线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 提交一个Callable任务,返回一个Future类型
        Future<Integer> future = threadPool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("新线程正在执行,处理相关的逻辑!");
                Thread.sleep(3000);
                int sum = 0;
                for (int i = 0; i < 100; i++) {
                    sum += i;
                }
                return sum;
            }
        });

        try {
            Thread.sleep(3000);//模拟本线程的一些任务
            //获取执行结果
            System.out.println(future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、使用场景

1、Tomcat内部采用了多线程,上百个用户同时访问同一个web应用,都会新开一个线程,调用到Servlet程序。若是不使用多线程,将串行操做,客户端将等待别人执行完才能访问。

2、异步请求,有两个任务Task a和Task b,单线程只能先进行a再进行b。

3、须要知道执行进度,好比说咱们常看到的进度条,任务执行到必定进度给new 一个变量,给变量+1.新开一个线程去轮询这个变量,反馈给客户端,这样就能够看到进度状况.

总之,不少地方都用到了多线程,多线程是为了充分利用cpu资源,当你发现一个业务逻辑执行效率特别低,耗时长,能够考虑使用多线程.

相关文章
相关标签/搜索