java创建多线程

java线程创建

三种创建方式:

  • 继承Thread类
  • 实现Runable接口
  • 实现Callable接口

一、继承Thread类:

  1. 继承Thread类
  2. 重写run方法,编写线程执行体
  3. 创建线程对象,调用start方法开启线程
package com.yuanyu.thread;

    public class TestThread extends Thread {
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i < 20; i++) {
                System.out.println("我在写代码------------"+i);
            }
        }

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

            //创建一个线程对象
            TestThread testThread = new TestThread();

            //调用start()开启线程
            testThread.start(); 
            //testThread.run(); 

            for (int i = 0; i < 20; i++) {
                System.out.println("我在学习多线程---------"+i);
            }
        }
    }

testThread.start()😕/两个线程同时执行

image

testThread.run()😕/run线程先执行

image

线程开启不一定立即执行,由CPU调度执行

多线程实现图片下载:

package com.yuanyu.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//实现多线程同步下载图片
public class TestThread extends Thread{
    
    private String url; //网络图片地址
    private String name; //保存的文件名

    public TestThread(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) {
        TestThread testThread1 = new TestThread("https://c-ssl.duitang.com/uploads/blog/202105/22/20210522104221_f81d8.thumb.1000_0.jpg","clx1.jpg");
        TestThread testThread2= new TestThread("https://c-ssl.duitang.com/uploads/blog/202105/22/20210522104221_f81d8.thumb.1000_0.jpg","clx2.jpg");
        TestThread testThread3 = new TestThread("https://c-ssl.duitang.com/uploads/blog/202105/22/20210522104221_f81d8.thumb.1000_0.jpg","clx3.jpg");

        testThread1.start(); //启动线程
        testThread2.start(); //启动线程
        testThread3.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方法出现问题");
        }
    }
}

image

image

思路:

  1. 创建一个下载器,通过FileUtils类的copyURLToFile方法将路径保存为文件;
    该方法需要导入IOjar包IOjar包下载地址
  2. 创建一个线程类继承Thread类,在类中添加需要的属性和对应构造方法并重写run方法;
  3. 创建一个主线程并实例化线程类,通过start方法开启线程

二、实现Runnable接口

步骤:

  1. 定义一个类去实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
package com.yuanyu.thread;

//创建线程方式二:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法
public class TestThread2 implements Runnable{
    @Override
    public void run() {
        //run()线程体
        for (int i = 0; i < 20; 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 < 20; i++) {
            System.out.println("我在学习多线程------------------"+i);
        }
    }
}

image

继承Thread类与Runnable接口的比较:

  1. 都需要重写run方法

  2. Thread类直接创建thread对象调用start方法启动线程 子类对象.start();

    Runnable接口需要通过一个线程对象来包含接口对象去调用start方法开启线程 new Thread(传入目标对象).start(); 两者本质都是一样的。

  3. 不建议使用继承Thread类创建多线程:为了避免OOP单继承的局限性

    而使用Runnable接口:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用

多线程操作同一对象:

package com.yuanyu.thread;

//多个线程同时操作同一个对象
//买火车票的例子
public class TestThread2 implements Runnable{
    private int ticketNums=10;

    @Override
    public void run() {
        while (true){
            if (ticketNums<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票");
        }
    }

    public static void main(String[] args) {
        TestThread2 testThread2 = new TestThread2();

        new Thread(testThread2,"小明").start();
        new Thread(testThread2,"小红").start();
        new Thread(testThread2,"小黑").start();
    }
}

image

通过运行结果可得出:同一张票被多人重复获取

多个线程操作一个资源的情况下,线程不安全,数据紊乱

龟兔赛跑问题:

package com.yuanyu.thread;

//实现龟兔赛跑
public class Race implements Runnable {
    private static String winner; //获胜者

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (Thread.currentThread().getName().equals("rabbit")&&(i%10==0)){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            boolean flag=gameOver(i);
            if (flag){
                break;
            }

            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }

    private boolean gameOver(int steps){
        if (winner != null){
            return true;
        }
        if (steps>=100){
            winner=Thread.currentThread().getName();
            System.out.println("The winner is "+winner);
            return true;
        }
        else {
            return false;
        }
    }

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"rabbit").start();
        new Thread(race,"turtle").start();
    }
}

image

三、实现Callable接口(了解即可)

步骤:

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常,函数定义返回值
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1=ser.submit(t1);
  6. 获取结果:boolean r1=result1.get()
  7. 关闭服务:ser.shudownNow();

用Callable实现多线程下载图片:

package com.yuanyu.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//线程创建方式三:实现Callable接口
public class TestCallable implements Callable<Boolean> {
    private String url;
    private String name;

    public TestCallable(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        DownLoad downLoad = new DownLoad();
        downLoad.download(url,name);
        System.out.println("下载了文件名为"+name);
        return true;
    }

    public static void main(String[] args) {
        TestCallable testCallable = new TestCallable("https://c-ssl.duitang.com/uploads/blog/202105/22/20210522104221_f81d8.thumb.1000_0.jpg","1.jpg");
        TestCallable testCallable2 = new TestCallable("https://c-ssl.duitang.com/uploads/blog/202105/22/20210522104221_f81d8.thumb.1000_0.jpg","1.jpg");

        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(2);
        //提交执行
        Future<Boolean> result1=ser.submit(testCallable);
        Future<Boolean> result2=ser.submit(testCallable2);
        //获取结果
        try {
            boolean results1=result1.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        try {
            boolean results2=result2.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //关闭服务
        ser.shutdownNow();

    }
}

class DownLoad{
    public void download(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("The download function is wrong.");
        }
    }
}

优点:

  1. 可以定义返回值
  2. 可以抛出异常
posted @ 2022-01-10 15:09  原语  阅读(315)  评论(0)    收藏  举报