Java------多线程基础Thread与Runnable模拟并发抢夺资源

线程与进程

操作系统中会存在多个进程:这些进程包括系统进程、以及用户进程。
系统进程指:操作系统内部建立的进程。
用户进程指:用户程序建立的进程。

进程和线程的区别:

1.进程和进程之间不共享内存,进程是在独立的内存空间中运行的。
2.而线程则可以共享系统分配给这个进程的内存空间。
3.线程不仅能共享进程的内存,也拥有自己的内存空间,这段空间叫做线程栈,是在建立线程时由系统分配的,主要用来保存线程内部所使用的数据。

start和run

Java通过Thread类将线程所必需的功能都封装了起来。
线程执行函数就是run()方法
Thread还有一个start()方法,这个方法是建立线程,调用strat()方法后,如果线程建立成功,则程序会自动调用Thread类的run()方法。

注:strat()方法,线程建立成功,会自动调用run方法。执行线程必须调用start()加入调度器中,不一定立即执行,系统安排调度分配执行,直接调用run()不是开启多线程,而是普通方法调用

任何继承Thread类的都可以通过调用start方法【建立】线程,如果希望运行自己编写线程的执行函数,则要覆盖Thread类的run()方法。

/**
 * 多线程测试
 * 第一种方法继承Thread
 * 多次执行结果不同,两个线程同时执行
 * strat()方法不保证立即运行,由CPU决定
 */
public class ThreadTest extends Thread {
    
    
    @Override
    public void run(){
    
    
        for (int i = 0;i<=10;i++){
    
    
            System.out.println(i);
        }
    }


    public void run2(){
    
    
        for (int i = 0;i<=10;i++){
    
    
            System.out.println(i+"run2.............");
        }
    }

    //显式无参构造
    ThreadTest(){
    
    
        System.out.println("创建了ThreadTest");
    }

    public static void main(String[] args) {
    
    
        ThreadTest threadTest = new ThreadTest();
        threadTest.start();
        threadTest.run2();
    }

}

图片下载案例

/**
 * 图片下载案例
 */
public class ThreadTestPictureDown {
    
    

    /**
     * 下载方法
     * @param url
     * @param name
     */
    public void download(String url,String name){
    
    
        try {
    
    
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
    
    
            e.printStackTrace();
            System.out.println("不合法的URL,下载失败");
        }
    }
}

本质上和之前的案例没什么区别,唯一有需要注意的地方是FileUtils这个工具类,能实现下载文件的功能。

public class TDownLoad extends Thread{
    
    
    private String url;
    private String name;


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

    @Override
    public void run() {
    
    
        ThreadTestPictureDown threadTestPictureDown = new ThreadTestPictureDown();
        threadTestPictureDown.download(url,name);
        System.out.println("下载"+name+"图片完成");
    }

    public static void main(String[] args) {
    
    
        TDownLoad t1 = new TDownLoad("https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF","t1.jpg");
        TDownLoad t2 = new TDownLoad("https://t7.baidu.com/it/u=4036010509,3445021118&fm=193&f=GIF","t2.jpg");
        TDownLoad t3 = new TDownLoad("https://t7.baidu.com/it/u=963301259,1982396977&fm=193&f=GIF","t3.jpg");
        t1.start();
        t2.start();
        t3.start();
    }
}

打印结果图片3、图片1、图片2

Thread和Runnable

能够通过继承Thread类、Runnable接口实现线程类,Thread类其实也实现了Runnable接口,Thread作为实现类功能更加强大,而Runnable接口源码中只有一个run方法。但是Java中只能够实现单继承,却可以实现多个接口,因此在使用时,注意合理选择。
在继承Thread类时,如果没有为这个类命名,会自动使用默认线程名Thread-N,N代表线程建立的顺序,是一个不重复的整数。

注意:通过实现Runnable接口创建线程类时,依然需要创建Thread对象,将线程类作为参数传入Thread对象中,并调用start方法,直接调用run方法并不能够启动线程,依然是在主线程中启动run方法。

此时可以多个代理。下面抢票小案例可以看出。

public class ThreadTest01 implements Runnable{
    
    
    @Override
    public void run(){
    
    
        for (int i = 0;i<=10;i++){
    
    
            System.out.println(i);
        }
    }
    public void run2(){
    
    
        for (int i = 0;i<=10;i++){
    
    
            System.out.println(i+"run2.............");
        }
    }

    //显式无参构造
    ThreadTest01(){
    
    
        System.out.println("创建了ThreadTest");
    }

    public static void main(String[] args) {
    
    
        ThreadTest01 threadTest01 = new ThreadTest01();
        new Thread(threadTest01).start();
        threadTest01.run2();
    }
}

抢票小案例

public class ThreadTest01 implements Runnable{
    
    
    private  int tickNums = 99;
    @Override
    public void run(){
    
    
        while (true){
    
    
          if (tickNums<0){
    
    
              break;
          }
          System.out.println(Thread.currentThread().getName()+"--->"+tickNums--);
        }
    }


    //显式无参构造
    ThreadTest01(){
    
    
        System.out.println("创建了ThreadTest");
    }

    public static void main(String[] args) {
    
    
        ThreadTest01 threadTest01 = new ThreadTest01();
        //多个代理,加入名称区分
        new Thread(threadTest01,"thread01").start();
        new Thread(threadTest01,"thread02").start();
        new Thread(threadTest01,"thread03").start();
    }
}

但这样是有问题的,现实情况中网络存在延迟,就可能出现负数问题。此时还没出现,下面加入线程睡眠200毫秒,模拟这种情况。

 @Override
    public void run(){
    
    
        while (true){
    
    
          if (tickNums<0){
    
    
              break;
          }
            try {
    
    
                Thread.sleep(200);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--->"+tickNums--);
        }
    }

这样在控制台打印时就会出现负数的情况。此时也算是并发问题,后续需要保证线程安全。runable接口可以共享资源,但也会出现资源抢夺的问题

Guess you like

Origin blog.csdn.net/cz_chen_zhuo/article/details/116797837