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

来源:互联网 发布:java 获取对象的大小 编辑:程序博客网 时间:2024/05/21 15:24

转载请注明出处: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;            }        }    }}
原创粉丝点击