Java多线程(二)认识Java里面的Thread

转载请注明出处:http://blog.csdn.net/github_39430101/article/details/77340996

创建线程的三种方法

java.lang.Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的的定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。

继承Thread类
public class TestThread extends Thread{
    @Override
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println("我是线程");
        }
    }
}

创建和启动线程:

public class ThreadApp{
    public static void main(String[] args){
        Thread thread = new TestThread(); //实例化线程
        thread.start();  //启动线程
    }
}
实现Runnable创建并启动线程

实现Runnable接口并重写run()方法来定义线程体,然后再创建线程的时候讲Runnable的的实例传入并启动线程。这样做的好处在于可以将线程与线程要执行的任务分离开减少耦合,同时Java是单继承的,定义一个类实现Runnable接口这样的做法可以更好的去实现其他父类或接口。因为接口是多继承关系。这里我们讲一下静态代理设计模式。

静态代理
  • 真实角色
  • 代理角色 :持有真实角色的引用
  • 二者实现相同的接口
public class StaticProxy {
    public static void main(String[] args) {
        //创建真实角色
        You you = new You();
        //创建代理角色+真实角色的引用
        Agency agency = new Agency(you);
        agency.renting();
    }
}
//创建一个租房的接口,里面有租房方法
interface Rent{
    //公共方法租房
    void renting();
}
//真实角色
class You implements Rent{
    @Override
    //租房
    public void renting(){
        System.out.println("我租房子");
    }
}
//代理角色,中介公司
class Agency implements Rent{
    Rent you;
    public Agency(){

    }
    public Agency(Rent you){
        this.you = you;
    }
    //租房前要做的
    private void before(){
        System.out.println("找中介公司");
    }
    private void after(){
        System.out.println("交房租");
    }
    @Override
    public void renting(){
        before();
        you.renting();
        after();
    }
}
//输出:
找中介公司
我租房子
交房租

Runnable相当于我们的租房接口,Thread类也是实现它的,我们只需要实现它并重写run()方法,其他的事情交给中介Thread类

//真实角色类
public class TestRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println("我是线程");
        }
    }
}

启动线程

public class ThreadApp{
    public static void main(String[] args){
        Runnable runnable = new TestRunnable();
        Thread thread = new Thread(runnable); //实例化线程并传入线程体
        thread.start(); //启动线程
    }
}
实现Callable接口

Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务。Callable和Runnable有几点不同:

  • Callable规定的方法是call(),而Runnable规定的方法是run()
  • call()方法可抛出异常,而run()方法是不能抛出异常的
  • Callable的任务执行后可返回值,运行Callable任务可拿到一个Future对象,而Runnable的任务是不能返回值的。Future标识异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
public class Call {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        };
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(5000);
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

线程API

Thread的静态方法currentThread方法可以用于获取运行当前代码片段的线程。

Thread current = Thread.currentThread();

获取线程信息

long getId()    //返回该线程的标识符
String getName()   //返回该线程的名称
int getPriority()  //返回线程的优先级
Thread.state getState()  //获取线程的状态
boolean isAlive() //测试线程是否处于活动状态
boolean isDaemon() //测试线程是否为守护线程
boolean isInterrupted() //测试线程是否已经中断

常用方法:

void run()  //创建该类的子类时必须实现的方法
void start()  //开启线程的方法
static void sleep(long t) //释放CPU的执行权,不释放锁。调用的目的是不让当前线程霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会
final void wait() //释放CPU的执行权,释放锁。当一个线程执行到wait()方法是,它就进入到一个和该对象相等的等待池(Waiting Pool)中,同时失去了对象的锁-暂时的,wait后还要返还对象锁。当前线程必须拥有对前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用
final void notify()/notifyAll() //唤醒在当前对象等待池中等待的第一个线程/所有线程。notify/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常
static void yield() //该方法用于使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片。
void join()  //该方法用于等待当前线程结束,是一个阻塞方法。

线程的状态

这里写图片描述

  • 新建状态(new):当线程对象创建后,即进入了新建状态,如:Thread t = new MyThread();
  • 就绪状态(Runnable):当调用线程对象的start()方法线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了等待CPU调度分配时间片的准备,并不是说执行了start()方法此线程就立即会执行。
  • 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此线程才开始真正执行,即进入到运行状态。
  • 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会再次被CPU调用。阻塞状态可分为三种:

    1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态
    2.同步阻塞:线程在获取synchronized同步锁(锁被其他线程所占用),它会进入同步阻塞状态
    3.其他阻塞:通过调用线程的sleep()或join()或发出了IO请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、后者IO处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):线程执行完了或者因为异常退出了run()方法,该线程结束生命周期
停止线程
  • 自然终止:线程体正常执行完毕
  • 外部干涉
    1.线程类中定义线程体使用的标识
    2.线程体使用该标识
    3.提供对外的方法改变该标识
    4.外部根据条件调用该方法
public class Demo {
    public static void main(String[] args) {
        Study s = new Study();
        new Thread(s).start();
        for(int i=0; i<100; i++){
            if(i == 50){
                s.stop();
            }
            System.out.println("main...."+i);
        }
    }
}
class Study implements Runnable{
    private boolean flag = true;
    @Override
    public void run(){
        while(flag){
            System.out.println("study thread...");
        }
    }
    public void stop(){
        this.flag = false;
    }
}

如果线程是阻塞的,则不能使用上面的方法来终止线程

public class ThreadInterrupt extends Thread {
  public void run() {
    try {
      sleep(50000); // 延迟50秒
    } catch (InterruptedException e) {
      System.out.println(e.getMessage());
    }
  }

  public static void main(String[] args) throws Exception {
    Thread thread = new ThreadInterrupt();
    thread.start();
    System.out.println("在50秒之内按任意键中断线程!");
    System.in.read();
    thread.interrupt();
    thread.join();
    System.out.println("线程已经退出!");
  }
}

线程阻塞

join
public class Demo extends Thread{
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread t = new Thread(demo);
        t.start();
        for(int i=0;i<1000;i++){
            if(i == 50){
                try {
                    t.join();  //main阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main............."+i);
        }
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("join....."+i);
        }
    }
}

这里写图片描述
当main执行到49的时候开始阻塞了,执行join。

yield
public class Demo extends Thread {
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread t = new Thread(demo);
        t.start();
        for(int i=0;i<1000;i++){
            if(i % 20 ==0){
                Thread.yield(); //暂停本线程
            }
            System.out.println("main............."+i);
        }
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("join....."+i);
        }
    }
}
sleep

休眠,不释放锁。常用于

  • 与时间相关:倒计时
  • 模拟网络延时

倒计时

import java.text.SimpleDateFormat;
import java.util.Date;

/*
 * 倒计时
 * 倒数10个数,一秒内打印一个
 */

public class Demo extends Thread {
    public static void main(String[] args) throws InterruptedException {
        Date endTime = new Date(System.currentTimeMillis()+10*1000);
        System.out.println(endTime);
        long end = endTime.getTime();
        while(true){
            //输出
            System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
            //等待一秒
            Thread.sleep(1000);
            //构建下一秒的时间
            endTime = new Date(endTime.getTime()-1000);
            //10秒内继续,否则退出
            if((end-10000)>endTime.getTime()){
                break;
            }
        }

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值