多线程-基础

线程的生命周期

Thread类

如果不给Thread对象传name参数,那么new出来的线程对象默认叫Thread-0,依次增加。如果有被自定义name的线程对象,那么下一个无指定name的对象序号不受影响,会叫Thread-01。

常见实现多线程的方法

  • 继承Thread类
    1.基础
public class Thread01 extends Thread {
    @Override
    public void run() {
        for (int i=0;i<5;i++)
        {
            System.out.println("我在看手机");
        }
    }
    public static void main(String[] args) {
        //创建Thread类子类对象
        Thread01 thread01=new Thread01();
        //调用start()方法,启用多线程,不保证立即运行
        thread01.start();
        //直接调用run()方法只是一个普通方法,start()才是开启多线程
        //thread01.run();
        for (int i=0;i<5;i++)//此时我在看手机和我在玩游戏各自进行。
        {
            System.out.println("我在玩游戏");
        }
    }
}
输出.png
  1. 多线程图片下载
import org.apache.commons.io.FileUtils;

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

public class PicDownloader {
    public void download(String url,String name)
    {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            System.out.println("下载失败");
        }
    }
}

public class TDownLoader extends Thread {
    private String url;
    private String name;
    public TDownLoader(String url,String name)
    {
        this.url=url;
        this.name=name;
    }

    @Override
    public void run() {
        PicDownloader picDownloader=new PicDownloader();
        picDownloader.download(url,name);
        System.out.println(name);
    }

    public static void main(String[] args) {
        TDownLoader tDownLoader1=new TDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682114283&di=434cb570d405250902076c4245dee68f&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D2247852322%2C986532796%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","house.jpg");
        TDownLoader tDownLoader2=new TDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682168382&di=a396192287a6522d2d999b0085dedba8&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","lion.jpg");
        TDownLoader tDownLoader3=new TDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682255390&di=283617ba468afa5d91d196b6ebc075e3&imgtype=0&src=http%3A%2F%2Ft9.baidu.com%2Fit%2Fu%3D583874135%2C70653437%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D3607%26h%3D2408","sheep.jpg");
        //启动多线程
        tDownLoader1.start();
        tDownLoader2.start();
        tDownLoader3.start();
        //三个线程各自运行,并不一定会按1,2,3的顺序进行
    }
}

输出.png
  • 实现Runnable接口
public class Runnable01 implements Runnable {
    @Override
    public void run() {
        for (int i=0;i<5;i++)
        {
            System.out.println("我在看手机");
        }

    }

    public static void main(String[] args) {
       //要调用start方法必须实例化Thread对象
        Runnable01 r=new Runnable01();
        new Thread(r).start();
        for (int i=0;i<5;i++)
        {
            System.out.println("我在玩游戏");
        }
    }
}

输出.png

优先使用接口,因为java为多接口,单继承

  • 模拟抢票测试多个线程之间的数据共享问题
public class My12306 implements Runnable {
    private Integer tickets;

    public My12306(Integer tickets) {
        this.tickets = tickets;
    }
    public My12306()
    {

    }
    @Override
    public void run() {
        //System.out.println("1111");
        while (tickets>0)
        {
            System.out.println(Thread.currentThread().getName()+" --> Ticket"+tickets--);
        }
        System.out.println("没有票了哦");
    }
    public static void main(String[] args) {
        My12306 t=new My12306(7);
        new Thread(t,"小明").start();
        new Thread(t,"小黄").start();
        new Thread(t,"小红").start();
    }
}

输出.png

如果把while循环里的内容更换成

 while (tickets>0)
        {
            System.out.println(Thread.currentThread().getName()+" --> Ticket"+tickets);
            tickets--;
        }

就会出现


结果.png

由于多个线程共享tickets数据,所以出现了三个ticket7,可能涉及到了线程安全的问题。

  • 模拟赛跑
public class Race implements Runnable{
    private String winner;
    @Override
    public void run() {
        for (int steps=1;steps<=100;steps++)
        {
            System.out.println(Thread.currentThread().getName()+"-->"+steps);
            if (GameOver(steps))
            {
                break;
            }
        }
    }
    public boolean GameOver(int steps)
    {
        if (winner!=null)
        {
            return true;
        }

        else
        {
            if (steps==100)
            {   winner=Thread.currentThread().getName();
                System.out.println("Winner is "+winner);
                return true;
            }
            else
            {
                return false;
            }
        }

    }

    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"小明").start();
        new Thread(race,"小红").start();
        new Thread(race,"小光").start();
    }
}

结果
  • 第三个实现多线程的方法 实现callable方法-简单实现

public class CDownLoader implements Callable<Boolean> {
    private String url;
    private String name;
    public CDownLoader(String url, String name)
    {
        this.url=url;
        this.name=name;
    }

    @Override
    public Boolean call() throws Exception {
        PicDownloader picDownloader=new PicDownloader();
        picDownloader.download(url,name);
        System.out.println(name);
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CDownLoader cDownLoader1=new CDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682114283&di=434cb570d405250902076c4245dee68f&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D2247852322%2C986532796%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","house.jpg");
        CDownLoader cDownLoader2=new CDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682168382&di=a396192287a6522d2d999b0085dedba8&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853","lion.jpg");
        CDownLoader cDownLoader3=new CDownLoader("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598682255390&di=283617ba468afa5d91d196b6ebc075e3&imgtype=0&src=http%3A%2F%2Ft9.baidu.com%2Fit%2Fu%3D583874135%2C70653437%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D3607%26h%3D2408","sheep.jpg");
        //创建执行服务
        ExecutorService service= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> result1=service.submit(cDownLoader1);
        Future<Boolean> result2=service.submit(cDownLoader2);
        Future<Boolean> result3=service.submit(cDownLoader3);
        //获取结果
        boolean r1=result1.get();
        boolean r2=result2.get();
        boolean r3=result3.get();
        //关闭服务
        service.shutdownNow();

    }
}

守护线程

线程分为两类 :守护线程(后台线程)和用户线程(前台线程),上述都是创建用户线程的方法。
守护线程为用户线程服务,用户线程结束之后守护线程也结束。

代码示例

public class Guardian {
    public static void main(String[] args) {
        Human human=new Human();
        Universe universe=new Universe();
        Thread thread1=new Thread(human);
        Thread thread2=new Thread(universe);
        thread1.start();
        thread2.start();
    }
}
 class Human implements Runnable
{

    @Override
    public void run() {
        for (int i=0;i<100;i++)
        {
            System.out.println("第"+i+"岁");
        }
        System.out.println("ByeBye");
    }
}
class Universe implements Runnable
{

    @Override
    public void run() {
        while (true)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我永远存在");
        }
    }
}

此时Universe的进程并不是守护进程,所以当Human进程结束之后Universe进程会一直进行,所以程序不会结束。


结果的一部分

使用

thread2.setDaemon(true);

将Universe转换为守护进程之后。


结果

结果

当Human进程结束之后,程序自动结束,Universe进程也结束。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容