线程初步学习-01

线程概述

什么是线程?

几乎 所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
也可以这么描述:一个程序运行后至少有一个进程,一个进程可以包含多个线程,但至少要包含一个线程

多线程的优势
  1. 多线程编程简单,效率高(能直接共享数据和资源,多进程不能)(系统城建进程时需要为该进程重新分配系统资源,但创建线程则代价小很多,因此使用多线程来实现多任务并发比多进程效率高。
  2. 适合于开发服务程序(如Web服务、聊天服务等)。
  3. 适合于开发有多种交互接口的程序
  4. 适合于有人机交互又有计算量的程序
  5. 减轻编写交互频繁、涉及面多的程序的困难(如网络监听窗口)
  6. 程序的吞吐量会得到改善
  7. 有多个处理器的系统,可以并发运行不同的线程(否则,任何时刻只有一个线程在运行,只是宏观上看是并发运行)

线程的创建和启动

继承Thread类创建线程类
  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run()方法称为线程执行
  2. 创建Thread子类的实例,即创建了线程对象。
  3. 调用线程对象的start()方法来启动该线程
package thread;

/**
 * @author Mike
 * @use 集成thread类创建线程
 */
public class FirstThread extends Thread {
    private int i;
    public void run(){
        for (;i<100;i++){
            //当线程集成thread类时,直接使用this即可获取当前线程
            //thread对象的getname()返回当前线程的名字
            //因此可以直接调用getname()方法返回当前线程的名字
            System.out.println(getName() + " " +i);
        }
    }

    public static void main(String[] args) {
        for (int i=0;i<100;i++){
            //调用thread的current()方法获取当前线程
            System.out.println(Thread.currentThread().getName()+ " " +i);
            if (i==20){
                new FirstThread().start();
                new FirstThread().start();
            }
        }
    }
}

实现Runnable 接口创建线程类
  1. 定义Runnable接口创建线程类
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
  3. 调用线程对象的start()方法来启动该线程
package thread;

/**
 * @author Mike
 * @use 实现Runnable接口创建线程类
 */
public class SecondThread implements Runnable {
    private int i;
    @Override
    public void run() {
        for (;i<100;i++){
            System.out.println(Thread.currentThread().getName()+ " "+ i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+ " "+ i);
            if (i == 20){
                SecondThread st = new SecondThread();
                //通过new Thread(target,name)方法创建新线程
                new Thread(st,"线程1").start();
                new Thread(st,"线程2").start();
            }
        }
    }
}

使用Callable 和 Future 创建线程
  1. 创建Callable接口的实现类,并且实现call()方法,该call()方法将作为线程的执行主体,可以理解为有返回值的run()方法
  2. 使用FutureTask对象作为Thread对象的target创建并启动新线程
  3. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
package thread;

import javax.swing.*;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * @author Mike
 * @use 使用Callable和Future创建线程
 */
public class ThirdThread {
    public static void main(String[] args) {
        //创建Callable对象
        ThirdThread tt=new ThirdThread();
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
           int i = 0;
           for(;i<100;i++){
               System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i);
           }
           //call有返回值
            return i;
        });
        for (int i = 0;i < 100; i++){
            System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i);
            if (i == 20){
                new Thread(task,"有返回值的线程").start();
            }
        }
        try{
            System.out.println("子线程的返回值:" + task.get());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

线程的生命周期

线程的声明周期
线程对象调用了start()方法之后,该线程处于就绪状态
就绪状态–>阻塞状态

  1. 线程调用sleep()方法主动放弃所占有的处理器资源
  2. 线程调用了一个阻塞式IO方法,在该方法返回前,该线程被阻塞
  3. 线程wait()在等待notify()时会处于阻塞状态
  4. 程序调用了线程的suspend方法将线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法。

阻塞状态–>就绪状态

  1. 调用sleep()方法的线程经过了指定时间
  2. 线程调用的阻塞式IO方法已经返回
  3. 线程成功的获得了试图取得的同步监视器
  4. 被notify()唤醒
  5. 处于挂起状态的线程被调用了resume()恢复方法

运行状态–>就绪状态
yield()方法

线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。就绪和运行之间的状态转换通常不受程序控制,而是由系统线程调度所决定。
线程死亡

  1. run()方法执行完毕,线程正常结束
  2. 线程抛出一个未捕获的Exception 或 Error
  3. 直接调用该线程的stop()方法来结束该线程–该方法容易导致死锁,通常不推荐使用。
  • 不要对处于死亡状态的线程调用start()方法,程序只能对新建的线程调用start()方法,对新建状态的线程两次调用start()方法也是错误的。都会引发IllegalThreadStateException异常

控制线程

wait(),notify()

两个方法配套使用,wait()使得线程进入阻塞状态

  1. 当wait()时,必须经过对应的notify(),才能重新进入就绪
  2. wait(long time),括号中的参数是毫秒值,超出时间或者notigy()调用都能进入就绪状态
    还有一些涉及到同步的内容在写同步的时候再说吧~
sleep()

sleep()方法可以让当前正在执行的线程暂停一段时间,并进入阻塞状态
1.sleep(long time)让当前正在执行的线程暂停time毫秒,并进入阻塞状态
2.还有一个sleep(long millis,int nanos)不常用,后面的nanos是微秒

package thread;

import java.util.Date;

/**
 * @author Mike
 * @use 线程睡眠
 */
public class SleepTest {
    public static void main(String[] args) throws InterruptedException {
        for (int i=0;i<10;i++){
            System.out.println("当前时间: "+ new Date());
            Thread.sleep(1000);//注意此处是毫秒为单位
        }
    }
}//停一秒输出一个时间

join()

当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。

  1. join():等待join进来的线程执行完成
  2. join(long mills):等待join进来的线程执行mills毫秒,如果她还没执行完。就不再等了,继续执行。
package thread;

/**
 * @author Mike
 * @use 加入线程
 */
public class JoinThread extends Thread {
    public JoinThread(String name){
        super(name);
    }
    //重写run()方法,定义线程执行体

    @Override
    public void run() {
        for ( int i = 0;i < 100;i++){
            System.out.println(getName() + " " +i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new JoinThread("新线程").start();
        for (int i = 0 ;i< 100;i++){
            if (i == 20){
                JoinThread jt = new JoinThread(("join进来的线程"));
                jt.start();
                jt.join();
            }
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    //从结果上来看,刚开始时新线程和main线程在并发执行,有新的线程join进来后,main处于等待状态,
    // 新线程和join线程并发执行
    //等join线程和新线程执行完之后继续执行main线程
    //有个问题,join线程先执行完是没问题的,但是为什么新线程会在main线程执行完之前执行完
}

yield()

和sleep()方法相似,他也可以让当前正在执行的线程暂停,但他不会阻塞该线程,只是将该线程转入就绪状态
关于sleep()和yield()的区别

  1. sleep()方法暂停当前的线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给更高优先级或者相同优先级的线程执行机会
  2. sleeep()方法声明抛出了InterruptException异常,所以调用sleep方法时要么捕获该异常,要么抛出异常,而yield()方法则没有抛出异常
  3. sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行
package thread;

/**
 * @author Mike
 * @use 线程让步
 */
public class YieldTest extends Thread {
    public YieldTest(String name){
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0;i<50;i++){
            System.out.println(getName()+ " "+i);
            if (i == 20){
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) {
        YieldTest yt1 = new YieldTest("高级");
        yt1.setPriority(Thread.MAX_PRIORITY);
        yt1.start();
        YieldTest yt2 = new YieldTest("低级");
        yt2.setPriority(Thread.MIN_PRIORITY);
        yt2.start();
    }
}

线程优先级setPriority(int newPriority)

此处注意优先级并不是绝对的概念,并不是优先级高的一定要执行完才会执行优先级低的,也并不一定时优先级高的一定先执行,这是一种概率问题,比如优先级高的线程可能有80%的概率会调用执行,优先级低的有20%,但还是有可能会先调用优先级低的。

后台线程

调用setDaemon(true)方法可以把指定线程设置成后台线程,如果前台线程都死亡,后台线程会自动死亡

package thread;

/**
 * @author Mike
 * @use 后台线程
 */
public class DaemonThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++){
            System.out.println(getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        DaemonThread dt = new DaemonThread();
        dt.setDaemon(true);
        dt.start();
        for (int i = 0 ;i < 10 ; i++){
            System.out.println(Thread.currentThread().getName()+" "+ i );
        }
    }
}
//此处发现

下面的链接是下一部分关于线程同步的
线程同步学习

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值