秋招准备-Java-并发编程-多线程基础(一)

1.线程的五个状态

2.线程图

3.多线程的实现方式

4.Thread常用方法


1.线程的五个状态

    1.新建:用new实例化一个线程对象。

    2.可运行:线程对象创建后,调用start()方法,通知jvm后,等待被线程调度选中。

    3.运行:可运行状态的线程被选中后,获得cpu的时间轮片,开始执行run()方法,处于运行状态。

    4.阻塞:阻塞状态指线程因某种原因放弃cpu的使用权,暂停运行。

        <1>等待阻塞:wait()方法后(发生在synchronized同步块内),放弃持锁,等待其他持锁线程notify()唤醒

        <2>同步阻塞:进入synchronized同步块时,无法持锁则被阻塞,等待持锁线程释放锁。

        <3>其他阻塞:sleep()方法的持锁阻塞,join()方法的阻塞等待。

    5.死亡:线程的run()方法,主线程main()方法结束,或者线程运行时遇到异常退出,则线程死亡。



2.线程状态转换图

    随时手画温习:




3.多线程的实现方式

    1.继承Thread类,重写run()方法

        Thread类实际也实现了Runnable方法,重写run()方法后,即可通过start()方法来启动线程。

    2.实现Runnable接口,重写run()方法,并实例化一个对象传入到Thread实例中

        1和2本质上是一样的,即通过实现Runnable接口来重写run()方法,然后实例化Thread对象来操纵线程。

    3.实现Callable接口,重写call()方法,通过线程池来执行实现了callable的实例对象

        Callable方法是可以提供返回值的线程实现,run()方法是void型的,而call()方法带有返回值,在通过线程池执行后,会返回一个Future对象,然后可以通过Future对象的一些方法,得到call()的返回值。

    ->这个问题可以转移的点有:

    1.通过分析继承Thread和实现Runnable的选择,来谈接口与继承的区别。

    2.通过谈到Callable接口,来详细说Callable,Future,FutureTask这几个类与其中的方法

    3.通过谈到Callable的实现需要通过线程池,然后去讲线程池。



4.Thread的常用方法

    1.start()方法与run()方法

        start()方法是一个本地方法,它将启动一个线程来执行run()方法,调用start()方法以后,线程变成可运行状态,等待JVM的调度。

        如果直接调用run()方法,那么这个run()方法就是一个普通方法,不会产生新的线程。


    2.sleep()方法

        sleep()方法是Thread类的静态方法,可以在指定毫秒数内让当前正在执行的线程休眠。

        sleep()通过对象来调用和通过类来调用是一样的,关键在于是在哪个线程里面调用它,理解了暂停当前线程是什么意思,才能知道sleep()要怎么用。

        sleep()如果是发生在同步块内,那么持锁着被sleep()暂停后不释放锁。

        一开始很困惑当前线程,因此写一段代码来解释当前线程的含义。

public class Main
{
	static class Test implements Runnable
	{
		public void run()
		{
			try {
			System.out.println("2当前线程:"+Thread.currentThread());
			Thread.sleep(2000);		//这里的当前线程是以Test实例对象为参数的线程
			}catch(Exception e) {}
		}
	}
	public static void main(String[] args)
	{
		//对于下面所有代码来说,当前线程都是主线程main
		Thread t1 = new Thread(new Test());
		Thread t2 = new Thread(new Test());
		System.out.println("1当前线程:"+Thread.currentThread());
		//在这里调用Thread.sleep()/t1,sleep()/t2.sleep(),暂停的都是main
		t1.start();
		//这里也同上
		t2.start();
		//这里也同上
		
	}
}


    3.join()方法

        join()方法是一个将几个异步执行的线程再次改回串行(按顺序)执行的方法。

        这里说的异步执行,意思等同于相互不干扰的执行,也可以说是同时执行。而串行则是按顺序一个接一个执行。

        在一个线程中调用另一个线程的join()方法,则会阻塞当前线程,并等到调用join()的线程执行完,才接触当前线程的阻塞。

        如果在主线程main中调用另一个线程的join,则会阻塞main,然后等到这个线程完成,才继续执行后面的内容。

        如果在线程T1中调用线程T2的join,则T1进入阻塞,开始执行T2,等T2执行完,T1恢复,再次被调度以后执行T2.join()后面的代码。

        用join()方法也可以实现一些简单的同步


    4.interrupt()方法

        这一系有三个方法:

        void interrupt(),boolean interrupted(),boolean isInterrupted(),

        interrupt()用来设置中断标志为true,当该线程的中断标值为true时,如果该线程处于阻塞(该线程是sleep()暂停的当前线程、是join()阻塞的当前线程、),那么该线程会抛出一个异常,然后退出阻塞(之后到哪里就看具体代码,比如阻塞语句处有捕获,则会捕获异常跳到catch语句)。但是interrupt()不能跳出由竞争同步锁产生的阻塞,所以synchronized的阻塞存在无限等待可能。

        isInterrupted()是用来返回相应线程的中断标值情况的,只返回,不更改值。

        interrupted()又是个静态方法,则代表它的作用对象是当前线程,用来返回当前线程此刻的中断标值,之后将值置为false

        写一段实验代码:

public class Main  
{  
    public static void main(String[] args) throws Exception  
    {  
        Object o = new Object();  
        Thread th1 = new Thread(new Runnable() {  
            public void run() {  
                synchronized(o) {  
                    long begintime = System.currentTimeMillis();  
                    try {  
                        Thread.sleep(5000);  
                    }catch(Exception e) {}  
                    long endtime = System.currentTimeMillis();  
                    System.out.println("th1结束,共计:"+(endtime-begintime));
                    }  
            }
        });  
        Thread th2 = new Thread(new Runnable() {  
            public void run() {  
                try {  
                synchronized(o) {  
                    System.out.println("th2拿到锁");  
                }}catch(Exception ex) {  
                    System.out.println("th2跳出阻塞");  
                }  
            }  
        });  
        th1.start();  
//      Thread.sleep(1000);  
//      th1.interrupt();  
        th2.start();  
        th2.interrupt();  
    }  
}

        这段代码中(就默认th1先执行吧),th2的interrupt()方法不会使th2在synchronized同步块阻塞时跳出,只会一直请求锁。而如果取消注释,即在主线程1s后调用th1的interrupt()方法,此时th1处在sleep()暂停中,所以会跳出这个阻塞,抛出异常,然后时间总计时间会在1000毫秒上,而不是5000.


    5.wait()、notify()、notifyAll()方法

        是从Object继承过来的方法,用在同步块上,实现单个条件队列的中断与唤醒。

        wait(),使拿锁者阻塞,释放锁,进入待唤醒队列

        notify()、notifyAll(),拿锁者唤醒某个(随机),或者全部待唤醒线程。

       使用:如在一个while(condition)下,满足这个condition条件的线程都不立即执行(就像有些先期工作还没做,先放放等满条件再试),然后调用wait()阻塞,释放锁,让其他线程先执行。然后再在其他线程的执行代码里,有另一个if(condition2),当condition2满足了,就可以notify()唤醒一个阻塞线程(等于先期工作做完了,可是试着做一做上面被阻塞的线程的工作了)。


    6.其他方法(不具体说了)

        yield():从running->runnable

        isAlive():对应线程的存活情况

        currentThread():返回当前线程(静态方法)

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值