二、多线程(1)线程基础

目录

一、什么是线程

二、如何定义线程

 1.继承java.lang.Thread类

2.实现Runnable接口

3.Future/Callable

三、实例化

四、启动线程

五、线程的生命周期

①新建

②就绪

③运行和阻塞

④死亡

六、常用方法

1.currentThread()方法

2.sleep()方法

3.yield()方法

4.getId()方法

5.isAlive()方法

6.join()方法 

7.getName()和setName() 

8.getPriority()和setPriority() 

9.setDaemon()和isDaemon() 


一、什么是线程

进程: 进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。

线程:是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

Java中线程是指java.lang.Thread类的一个实例或线程的执行。使用java.lang.Thread或java.lang.Runnable接口编写代码定义、实例化、启动新线程。
  Java中每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行。main()方法运行在一个线程内,称为主线程。一旦创建一个新的线程,就产生一个新的调用栈。
  线程分为两类:用户线程和守候线程。当所有用户线程执行完毕后,JVM自动关闭。但是守候线程却不独立与JVM,守候线程一般是有操作系统或用户自己创建的。

二、如何定义线程

 

Callable是一种更好的抽象:它认为主入口点(call())将返回一个值,并可能抛出一个异常

  • extends Thread
  • implements Runnable
  • 使用Future和Callable  

 1.继承java.lang.Thread类

public class Mythread extends Thread{
	public void run() {
		for (int i=0;i<10;i++) {
			System.out.println("hello!");
		}
	}
}
public class ThreadDome1 {
	public static void main(String[] args) {
		Thread t = new Mythread();
		Thread t1 = new Mythread1();
		/*
		 * 当start方法执行后,线程纳入线程调度。
		 * 一旦被分配CPU时间片,线程就会自动调用run方法
		 * 
		 */
		t.start();       //线程启动时调用start方法
		t1.start();
	}
}

②内部类创建

Thread t = new Thread() {
			public void run() {
				for(int i=0;i<10;i++) {
					System.out.println("线程");
				}
			}
		};

2.实现Runnable接口

public class ThreadDome {
	MyRunnable R = new MyRunnable();
	MyRunnable1 R2 = new MyRunnable1();
	
	Thread A = new Thread(R);
	Thread B = new Thread(R2);
}
class MyRunnable implements Runnable{
	public void run() {
		for (int i=0;i<10;i++) {
			System.out.println("11111");
		}
	}
}
class MyRunnable1 implements Runnable{
	public void run() {
		for(int i=0;i<10;i++ ) {
			System.out.println("22222");
		}
	}
}

②内部类

Runnable r = new Runnable() {
			public void run() {
				for(int i=0;i<10;i++) {
					System.out.println("线程");
				}
			}
		};
		Thread t = new Thread(r);

3.Future/Callable

import java.util.concurrent.*;
public class Test {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Task task = new Task();
        Future<Integer> future = executorService.submit(task);
        executorService.shutdown();
        
        System.out.println("主线程在执行任务...");
        try {
            Thread.sleep(2000);
        } catch(InterruptedException ex) {
            ex.printStackTrace();
        }
         
        try {
            System.out.println("task运行结果:"+future.get());
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        }  
        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在执行任务...");
        //模拟任务耗时
        Thread.sleep(5000);
        return 1000;
    }
}


//运行结果
子线程在执行任务...
主线程在执行任务...
task运行结果:1000
所有任务执行完毕

三、实例化

1. 如果是扩展了java.lang.Thread类的线程,则直接调用new即可。
2. 如果是实现了jav.lang.Runnable接口的类,则调用Thread的构造方法:
  Thread(Runnable target)
  Thread(Runnable target,String name)
  Thread(ThreadGroup group, Runnable target)
  Thread(ThreadGroup group, Runnable target, String name)
  Thread(ThreadGroup group, Runnable target, String name, long stackSize)

四、启动线程

在线程的Thread对象上调用start()方法

注意:

子线程一旦启动,其地位和主线程是一样的,所以一旦主线程结束了,子线程不会受影响,不会跟着结束;

线程对象的isAlive()方法在就绪,运行,阻塞时返回true,在新建,死亡时返回false;

对已经死亡的线程调用start()是无效的,会抛出异常。 死亡的线程不可再次作为线程来执行;

对于新建的线程,调用两次start()方法也会抛出异常;

五、线程的生命周期

①新建

程序使用new会新建一个线程,new出的对象跟普通对象一样,JVM会为其分配内存,初始化成员变量等,此时线程并没有运行,而是就是新建状态。

②就绪

当线程对象调用start后,线程将进入就绪状态。JVM会为其创建函数调度栈和计数器,但此时线程依然没有运行,而是等待获取CPU执行片

③运行和阻塞

当就绪状态的线程获取了CPU执行片的之后,就进入运行状态, 但是在执行过程中,可能会因为以下原因使线程进入阻塞状态:

  • CPU执行片已经用完,JVM切换到其他线程执行
  • 线程调用sleep()
  • 线程调用了阻塞IO方法,该方法返回之前,线程会一直阻塞
  • 线程试图获取被其他线程持有的同步监视器
  • 线程在等待某个通知
  • 程序调用了线程的suspend()将线程挂起。(容易死锁,不推荐)

线程从运行进入阻塞状态之后,接着只能继续阻塞或者再次进入就绪状态,下面情况会使线程由阻塞状态重新进入就绪状态:

  • 线程调用的slee()经过了指定时间
  • 线程调用的阻塞IO方法返回
  • 线程成功获取同步监视器
  • 线程收到其他线程发出的通知
  • 被挂起(suspend)的线程又被程序调用了resume方法

④死亡

程结束后就处于死亡状态,线程会以如下三种方式结束,

  • run()或call()正常执行完成,线程正常结束
  • 线程抛出一个未捕获的Exception或Error
  • 直接调用线程的stop()方法结束线程,容易死锁

六、常用方法

1.currentThread()方法

Thread.currentThread()  返回当前运行的线程的引用

2.sleep()方法

方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象

3.yield()方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

4.getId()方法

getId()的作用是取得线程的唯一标识

5.isAlive()方法

方法isAlive()的功能是判断当前线程是否处于活动状态

活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。

6.join()方法 

方法join()的作用是等待线程对象销毁。

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

7.getName()setName() 

用来得到或者设置线程名称

8.getPriority()setPriority() 

用来获取和设置线程优先级。

Java中线程的优先级分为1-10这10个等级,如果小于1或大于10则JDK抛出IllegalArgumentException()的异常,默认优先级是5。在Java中线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。注意程序正确性不能依赖线程的优先级高低,因为操作系统可以完全不理会Java线程对于优先级的决定。

9.setDaemon()isDaemon() 

用来设置线程是否成为守护线程和判断线程是否是守护线程。

Java中有两种线程:一种是用户线程,另一种是守护线程。当进程中不存在非守护线程了,则守护线程自动销毁。通过setDaemon(true)设置线程为后台线程。注意thread.setDaemon(true)必须在thread.start()之前设置,否则会报IllegalThreadStateException异常;在Daemon线程中产生的新线程也是Daemon的;在使用ExecutorService等多线程框架时,会把守护线程转换为用户线程,并且也会把优先级设置为Thread.NORM_PRIORITY。在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。

 

参考文章:

https://www.cnblogs.com/x-you/p/8608604.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值