java简单使用多线程

Posted 冷血~多好

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java简单使用多线程相关的知识,希望对你有一定的参考价值。

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
   如:运行中的QQ,运行中的MP3播放器
          程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。

  • 若一个进程同一时间 并行执行多个线程,就是支持多线程的
  • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
  • 一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以访问相同的变量和对象。
  • 这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患

单核CPU 和多核CPU 的理解

单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有  收了费才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费)。但是因为CPU时间单元特别短,因此感觉不出来。
如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()
垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。


 并行与并发


并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

线程 的创建和启动

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。

Thread类的特性

  1. 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为 线程体
  2. 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

Thread类构造器

Thread() :创建新的Thread对象
Thread(String threadname): :创建线程并指定线程实例名
Thread(Runnable target) :指定创建线程的目标对象,它实现了Runnable接中的run方法
Thread(Runnable target, String name) :创建新的Thread对象

.线程创建

三种方式:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

方式一: 继承Thread类 类
1) 定义子类继承Thread类。
2) 子类中重写Thread类中的run方法。
3) 创建Thread子类对象,即创建了线程对象。
4) 调用线程对象start方法:启动线程,调用run方法。



//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程

//总结:注意,线程开启不一定立即执行,由cpu调度执行
public class TestThread1 extends Thread

    @Override
    public void run() 
        super.run();
        //run方法线程体
        for(int i=0;i<20;i++)
            System.out.println("我在看代码----"+i);
        
    

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

        //创建一个线程对象
        TestThread1 testThread1=new TestThread1();
        //调用start()方法开启线程
        testThread1.start();

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

注意点:
1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU
调度决定。
3. 想要启动多线程,必须调用start方法。
4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上
的异常“IllegalThreadStateException”

 方式二:实现Runnable 接口

1) 定义子类,实现Runnable接口。
2) 子类中重写Runnable接口中的run方法。
3) 通过Thread类含参构造器创建线程对象。
4) 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
5) 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

//创建线程方式:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable


    public void run() 
        //run方法线程体
        for(int i=0;i<200;i++)
            System.out.println("我在看代码----"+i);
        

    

    public static void main(String[] args) 
      //创建runnable接口的实现类对象
        TestThread3 testThread3=new TestThread3();
        //创建线程对象,通过线程对象来开启我们的线程,代理
    //    Thread thread=new Thread(testThread3);
    //    thread.start();
        new Thread(testThread3).start();
        for (int i = 0; i < 1000; i++) 
            System.out.println("我在学习多线程---"+i);
        



    
*例子:买火车票 */
public class TestThread4 implements Runnable
    //票数
    private int ticketNums = 10;

    @Override
    public void run() 
        while (true)
            if(ticketNums<=0) break;

            try  //模拟延时
                Thread.sleep(200);
             catch (InterruptedException e) 
                e.printStackTrace();
            

            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        
    
    public static void main(String[] args) 
        TestThread4 ticket = new TestThread4();
        new Thread(ticket,"小明").start();
        new Thread(ticket,"老师").start();
        new Thread(ticket,"黄牛党").start();
    
    //问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
//龟兔赛跑
public class Race implements Runnable
    //胜利者
    private static String winner;

    @Override
    public void run() 
        for (int i = 1; i <= 100; i++) 

            boolean flag=gameOver(i);//判断比赛是否结束
            if(flag) break;

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

            //模拟兔子休息
            if(Thread.currentThread().getName().equals("兔子")&& i%10==0)
                try 
                    Thread.sleep(100);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        
    

    //判断是否完成比赛
    private boolean gameOver(int steps)
        if(winner!=null)
            return true;
        
            if (steps==100)
                winner=Thread.currentThread().getName();
                System.out.println("winner is"+winner);
                return true;
            
        
        return false;
    

    public static void main(String[] args) 
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    

继承方式和实现方式的联系与 区别

区别
 继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
实现方式的好处
 避免了单继承的局限性
多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源

Thread 类的有关方法

  1. void start(): 启动线程,并执行对象的run()方法
  2. run(): 线程在被调度时执行的操作
  3. String getName(): 返回线程的名称
  4. void setName(String name):设置该线程名称
  5. static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
  6. static void yield(): : 线程让步暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程若队列中没有同优先级的线程,忽略此方法
  7. join() : : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止低优先级的线程也可以获得执行
  8. static void sleep(long millis) :(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
  9. 抛出InterruptedException异常
  10. stop(): 强制线程生命期结束,不推荐使用
  11. boolean isAlive(): : 返回boolean,判断线程是否还活着

方法3:实现Callable接口,需要返回值类型

重写call方法,需要抛出异常

创建目标对象

创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);

提交执行:Future result1 = ser.submit(11);

获取结果:boolean r1 = result1.get()

关闭服务:ser.shutdownNow();
 


import org.apache.commons.io.FileUtils;

import java.io.File;
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;
    

    public Boolean call() throws Exception 
        WebDownLoader2 webDownLoader=new WebDownLoader2();
        webDownLoader.downloader(url,name);
        System.out.println("下载了文件名为"+name);
        return true;
    

    public static void main(String[] args) throws ExecutionException, InterruptedException 
        TestCallable testThread21=new  TestCallable("http://wanwan.group/images/index0.jpg","1.jpg");
        TestCallable testThread22=new  TestCallable("http://wanwan.group/images/index0.jpg","2.jpg");
        TestCallable testThread23=new  TestCallable("http://wanwan.group/images/index0.jpg","3.jpg");
        TestCallable testThread24=new  TestCallable("http://wanwan.group/images/index0.jpg","4.jpg");
        TestCallable testThread25=new  TestCallable("http://wanwan.group/images/index0.jpg","5.jpg");
        TestCallable testThread26=new  TestCallable("http://wanwan.group/images/index0.jpg","6.jpg");

        //创建执行服务
        ExecutorService service= Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> result=service.submit(testThread21);
        Future<Boolean> result2=service.submit(testThread22);
        Future<Boolean> result3=service.submit(testThread23);

        //获取结果
        boolean rs1=result.get();
        boolean rs2=result2.get();
        boolean rs3=result3.get();

        //关闭服务
        service.shutdownNow();

    

//下载器
class WebDownLoader2

    //下载方法
    public void downloader(String url,String name)
        try 
            FileUtils.copyURLToFile(new URL(url),new File(name));
         catch (Exception e) 
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        


    



线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转化为就绪状态
  • 让CPU重新调度,但礼让不一定成功,看CPU心情

public class TestYield 
    public static void main(String[] args) 
        MyYield myYield = new MyYield(); //一个对象两个线程
        //礼让不一定成功
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    


class MyYield implements Runnable

    @Override
    public void run() 
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield(); //礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    

线程插队

  • Join合并线程,待线程执行完成后,在执行其他线程,其他线程堵塞
/*
测试join方法,线程插队,强制执行,阻塞其他线程,尽量少用
 */
public class TestJoin implements Runnable
    @Override
    public void run() 
        for (int i = 0; i <= 1000; i++) 
            System.out.println("线程vip来了"+i);
        
    

    public static void main(String[] args) throws InterruptedException 
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start(); //启动我们的线程

        //主线程
        for (int i = 0; i <= 500; i++) 
            if(i==200)
                thread.join();//插队,等线程执行完
            
            System.out.println("main"+i);
        
    

线程的优先级


线程 的 优先级等级
MAX_PRIORITY :10
MIN _PRIORITY :1
NORM_PRIORITY :5

 涉及的方法
getPriority() : :返回线程优先值
setPriority(int newPriority) : :改变线程的优先级
 说明:
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调

// 优先级
public class TestPriority
    
    public static void main(String[] args) 
        //main 默认优先级 5
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        
        MyPriority myPriority = new MyPriority();

        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);

        //先设置优先级,再启动
        t1.start();  //Thread-0 默认优先级 5

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY); //最大优先级
        t4.start();

        t5.setPriority(3);
        t5.start();
    


class MyPriority implements Runnable
    @Override
    public void run() 
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    

守护线程

  • 线程分为用户线程守护线程(daemon)
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待…

//测试守护线程 daemon
public class TestDaemon 

    public static void main(String[] args) 
        God god = new God();
        You you = new You();

        //上帝是守护线程,用户线程结束自己也结束
        Thread thread = new Thread(god); 
        thread.setDaemon(true); //设置为守护线程 默认是false(表示用户线程)
        
        thread.start(); //守护线程启动

        new Thread(you).start(); //用户线程启动
    



class God implements Runnable
    @Override
    public void run() 
        while (true)
            System.out.println("上帝保佑着你");
        
    


class You implements Runnable
    @Override
    public void run() 
        for (int i = 0; i < 36500; i++) 
            System.out.println("你度过了开心的第"+i+"天");
        
        System.out.println("-==goodbye,world!==-");
    

以上是关于java简单使用多线程的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程详解我与多线程的第一次接触

不停车收费系统

不停车收费系统

多线程部分笔记

Java 信号量代码演练

cpu的多少线程是啥意思啊?