Java多线程(一)

一、普通方法调用和多线程

二、进程(Process)和线程(Thread)。

1、提起进程,就不得不说程序。程序是指令和数据的有序集合,其本身没有任何运行意义,是一个静态概念。

2、进程则是执行程序的一次执行过程,是一个动态概念。是系统资源分配的单位。

3、通常在一个进程中包含多个线程,当然一个进程至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。

注意:很多多线程是模拟出来的,真正的多线程是指有多个cup,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,故有同时执行的错觉。

三、三种创建方式:

1、Thread class——继承Thread类(不推荐使用

①自定义线程类继承Thread类

②重写run()方法,编写线程执行体

③创建线程对象,调用start()方法启动线程。

代码:

package java_01.com;
/**
 * 多线程
 * */
//创建线程的方式一:继承Thread类,重写run()方法,调用start()开启线程。
public class TestThread extends Thread {
    @Override
    public void run(){
        //run方法线程体
        for(int i=0;i<200;i++){
            System.out.println("我在学习多线程"+i);
        }
     }

    public static void main(String[] args) {
        //main()线程,主线程

        //创建一个线程对象
       TestThread testThread = new TestThread();
       //调用start()开启线程
       testThread.start();

       for(int i=0;i<200;i++){
           System.out.println("我在打游戏"+i);
       }
    }
}

运行:

例:使用Thread,多线程同步下载图片

在src下创建lib包下载并导入commons-io-2.6.jar包

代码:

package java_01.com;

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/*
练习Thread,实现多线程同步下载图片*
*/
public class TestThread1 extends Thread{
    private String url;//网络图片地址
    private String name;//保存的文件名称

    public TestThread1(String url,String name){
        this.url=url;
        this.name=name;
    }
//下载图片线程的执行体
    @Override
    public void run(){
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        TestThread1 t1 = new TestThread1("https://pics7.baidu.com/feed/aec379310a55b319177d007a03928a2ccefc17fa.jpeg?token=718d823c64f6ce30de333d6f9d37da59","1.jpg");
        TestThread1 t2 = new TestThread1("https://pics2.baidu.com/feed/4bed2e738bd4b31ce423c63ac7ed2f759f2ff8dd.jpeg?token=cb277435332c155244f9330512dc33e4","2.jpg");
        TestThread1 t3 = new TestThread1("https://pics2.baidu.com/feed/9d82d158ccbf6c810130f973fd05b93f32fa40ef.jpeg?token=498f20a4caa55065f9ca2fdaf709c49c","3.jpg");

        //先下载t1
        t1.start();
        //然后下载t2
        t2.start();
        //最后下载t3
        t3.start();
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch(IOException e){
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现的问题");
        }
    }
}

运行:

2、Runnable接口——实现Runnable接口(推荐使用

 ①实现Runnable接口。

 ②重写run方法。

 ③执行线程需要丢入Runnable实现类,调用start()方法。

代码:

package java_01.com;

//创建线程方式2:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start()方法。
public class TestThread2 implements Runnable{

    @Override
    public void run(){
        //run方法线程体
        for(int i=0;i<200;i++){
            System.out.println("我在学习多线程"+i);
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        TestThread2 testThread2 = new TestThread2();
        //创建线程对象,通过线程对象来开启我们的线程,代理
//        Thread thread = new Thread(testThread2);
//        thread.start();
        new Thread(testThread2).start();
        for(int i=0;i<200;i++){
            System.out.println("我在打游戏"+i);
        }
    }
}

运行:

 实现线程方法1和2对比:

②不同点:方法1继承了Thread类,直接创建了一个Thread对象来调用start方法,方法2则需要一个代理,通过一个Thread把Runnable接口的实现类丢进去调用start,本质上都相同。在方法1中Thread也是实现了Runnable接口。

①共同点:都重写了run方法

例:

代码:

package java_01.com;

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/*
练习Thread,实现多线程同步下载图片*
*/
public class TestThread1 implements Runnable{
    private String url;//网络图片地址
    private String name;//保存的文件名称

    public TestThread1(String url,String name){
        this.url=url;
        this.name=name;
    }
//下载图片线程的执行体
    @Override
    public void run(){
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        TestThread1 t1 = new TestThread1("https://pics7.baidu.com/feed/aec379310a55b319177d007a03928a2ccefc17fa.jpeg?token=718d823c64f6ce30de333d6f9d37da59","1.jpg");
        TestThread1 t2 = new TestThread1("https://pics2.baidu.com/feed/4bed2e738bd4b31ce423c63ac7ed2f759f2ff8dd.jpeg?token=cb277435332c155244f9330512dc33e4","2.jpg");
        TestThread1 t3 = new TestThread1("https://pics2.baidu.com/feed/9d82d158ccbf6c810130f973fd05b93f32fa40ef.jpeg?token=498f20a4caa55065f9ca2fdaf709c49c","3.jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch(IOException e){
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现的问题");
        }
    }
}

运行: 

小结前两种创建线程的方式:

①继承Thread类:

    a.子类继承Thread类具备多线程能力。

    b.启动线程:子类对象.start。

    c.不建议使用:避免OOP单继承局限性。

②实现Runnable接口

   a.实现接口Runnable具有多线程能力。

   b.启动线程:传入目标对象+Thread对象.start()。

   c.推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。

3、Callable接口——实现Callable接口

①Callable接口的特点:类似于Runnable接口,但是Runnable没有返回值,并且无法抛出经过检查的异常,而Callable依赖FutureTask类获取返回结果。

没有线程池:

代码演示:

public class Callabledemo{
   public static void main(String[] args) throws Exception{
      MyThread = new MyThread();
      FutureTask<Integer> result = new FutureTask<Integer>(mt);
      new Thread(result).start();
      // 获取运算结果是同步过程,即call方法执行完成,才能获取结果
      Integer sum = result.get();
      System.out.println(sum);
   }
}
class MyThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}

单个线程池:用ExecutorService、Callable、Future实现有返回值结果的线程

ExecutorService提供了submit()方法,传递了一个Callable或Runnable,返回Future,如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

public class MyCallable implements Callable<String> {
    /**
     * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
     */
    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName() + "   执行callable的call方法");
        return "result";
    }
 
    public static void main(String[] args) {
        test1();
    }
 
    /**
     * 单个线程
     */
    public static void test1() {
        // 1.创建固定大小的线程池
        ExecutorService es = Executors.newFixedThreadPool(1);
        // 2.提交线程任务,用Future接口接受返回的实现类
        Future<String> future = es.submit(new MyCallable());
        // 3.关闭线程池
        es.shutdown();
        // 4.调用future.get()获取callable执行完成的返回结果
        String result;
        try {
            result = future.get();
            System.out.println(Thread.currentThread().getName() + "\t" + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
结果:
pool-1-thread-1 执行callable的call方法
main result

多个线程的执行返回结果:

public class MyCallable implements Callable<String> {
 
    /**
     * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
     */
    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName() + "   执行callable的call方法");
        return "result";
    }
 
    public static void main(String[] args) {
        test2();
    }
/**
     * 多个线程
     */
    public static void test2() {
        // 1.创建固定大小的线程池(5个)
        int threadNum = 5;
        ExecutorService es = Executors.newFixedThreadPool(threadNum);
        // 2.提交线程任务,用Future接口接受返回的实现类
        List<Future<String>> futures = new ArrayList<Future<String>>(threadNum);
        for (int i = 0; i < threadNum; i++) {
            Future<String> future = es.submit(new MyCallable());
            futures.add(future);
        }
        // 3.关闭线程池
        es.shutdown();
        // 4.调用future.get()获取callable执行完成的返回结果
        for (Future<String> future : futures) {
            try {
                String result = future.get();
                System.out.println(Thread.currentThread().getName() + "\t" + result);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
 
}
结果:
pool-1-thread-1 执行callable的call方法
pool-1-thread-2 执行callable的call方法
pool-1-thread-4 执行callable的call方法
pool-1-thread-3 执行callable的call方法
main result
pool-1-thread-5 执行callable的call方法
main result
main result
main result
main result

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值