-
1、进程与线程
定义:
进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统构成的基础。每个正在系统中运行的程序都是一个进程。
线程:线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
区别:
1、进程是所有线程的集合,每一个线程是进程中的一条执行路径;
2、进程是资源分配的最小单位,线程是程序执行的最小单位;
3、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多;
4、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
-
2、使用场景及实现方式
使用场景:
1、后台任务,例如:定时向大量(100w以上)的用户发送邮件、短信,迅雷多线程下载。
2、异步处理,例如:发微博、记录日志等;
3、分布式计算。
实现方式:
1、继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。
代码:
扫描二维码关注公众号,回复: 4858427 查看本文章class CreateThread extends Thread { // run方法中编写 多线程需要执行的代码 public void run() { for (int i = 0; i< 10; i++) { System.out.println("i:" + i); } } } public class ThreadDemo { public static void main(String[] args) { System.out.println("-----多线程创建开始-----"); // 1.创建一个线程 CreateThread createThread = new CreateThread(); // 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法 System.out.println("-----多线程创建启动-----"); createThread.start(); System.out.println("-----多线程创建结束-----"); } }
2、实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread(注:Java语言的特点“单根继承”),此时,可以实现一个Runnable接口。
代码:
//功能描述:(创建多线程例子-Thread类 重写run方法) class CreateRunnable implements Runnable { @Override publicvoid run() { for (inti = 0; i< 10; i++) { System.out.println("i:" + i); } } } //功能描述:(创建多线程例子-Thread类 重写run方法) public class ThreadDemo2 { public static void main(String[] args) { System.out.println("-----多线程创建开始-----"); // 1.创建一个线程 CreateRunnable createThread = new CreateRunnable(); // 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法 System.out.println("-----多线程创建启动-----"); Thread thread = new Thread(createThread); thread.start(); System.out.println("-----多线程创建结束-----"); } }
3、使用匿名内部类方式
代码:
public class InnerClassThread { public static void main(String[] args) { System.out.println("-----多线程创建开始-----"); Thread thread = new Thread(new Runnable() { public void run() { for (int i = 0; i< 10; i++) { System.out.println("i:" + i); } } }); thread.start(); System.out.println("-----多线程创建结束-----"); } }
-
3、线程的运行状态
状态分类:
Java中的线程大致存在六种状态,有的说是五种(除去了“超时等待”)。
1、初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2、运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
3、阻塞(BLOCKED):表示线程阻塞于锁。
4、等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5、超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒
6、终止(TERMINATED):表示该线程已经执行完毕。
状态切换图:
-
4、线程的API方法
Thread类中的属性:
private volatile String name;//线程的名称 private int priority;//线程的优先级,最小1、默认5、最大10 private boolean daemon = false;//是否是守护线程 private Runnable target; //该线程要执行的任务 private ThreadGroup group; //该线程归属的线程组 private ClassLoader contextClassLoader; //加载器,默认为AppClassLoader private static int threadInitNumber; //数字标识匿名线程 ThreadLocal.ThreadLocalMap threadLocals = null; //线程私有变量 private long stackSize; //分配给线程的栈空间大小,默认0(不限制) private long tid; //线程id private static long threadSeqNumber; //线程序列 private volatile int threadStatus = 0; //线程运行状态,默认是0(NEW) volatile Object parkBlocker; //java.util.concurrent.locks.LockSupport.park使用 private volatile Interruptible blocker; //枚举字段,interrupt方法使用
Thread的构造方法:
public Thread(); public Thread(Runnable target); public Thread(Runnable target,String name); public Thread(String name); public Thread(ThreadGroup group, Runnable target); public Thread(ThreadGroup group, Runnable target, String name); /*分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。*/ public Thread(ThreadGroup group, Runnable target, String name, long stackSize) //ThreadGroup如果不指定,则为当前线程(即启动该线程的线程)的ThreadGroup;Runnable就是该线程将要执行的类, //分两种情况,一种是继承了Thread类,一种是实现了Runnable接口;name就是线程的名称,如果不指定则为"Thread-索引号(索引从0开始)";stackSize是JVM分配给线程的栈空间大小,默认0(不限制)
Thread中的关于自身的API:
//返回对当前正在执行的线程对象的引用 public static native Thread currentThread() //测试当前线程是否中断 public static boolean interrupted() //使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行) public static native void sleep(long millis) throws InterruptedException; //获取该线程状态,返回值为枚举,包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED public State getState() //判断该线程是否存活,标准是线程已启动但还没有执行完毕 public final native boolean isAlive() //获取当前jvm中该线程的唯一标示 public long getId() //判断该线程是否是守护线程 public final boolean isDaemon() //设置线程的名称 public final synchronized void setName(String name) //获取线程的名称 public final String getName()
Thread类中关于执行的API:
//正确的启动一个线程的方法。先完成一些初始化动作,然后会通知JVM调用线程的run()方法 public synchronized void start() //只是单纯地执行线程代码,并没有启动一个新的线程 public void run() //保证指定时间以内,在JVM中只有该线程在运行。millis是等待的毫秒数,nanos是微秒数(参数可省)。 //如果millis填0,则一直等到当前线程执行完毕,JVM才会调度其他线程执行 public final synchronized void join(long millis, int nanos) //让该线程运行结束之前做下自身资源清理工作 private void exit() //当该线程让出CPU,给其他线程一个获取调度的机会,但是不确保一定会调度到其他线程上 public static native void yield(); //判断该线程中断标识是否为true,返回该标识并将标识置为false public static boolean interrupted() //判断该线程中断标识是否为true,返回该标识。其实该方法和interrupted()方法底层调用的都是 //isInterrupted(boolean isClearState),只不过参数不同而已 public boolean isInterrupted() //它不会中断一个正在运行的线程,实际上是在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞 //的状态。更确切的说,如果线程被wait、join或sleep阻塞时,那么它将接收到一个中断异常 //(InterruptedException),我们可以在异常块里调用本方法,从而提早地终结被阻塞状态。如果线程没有 //被阻塞,这时调用interrupt()将不起作用,仅仅是设置中断标志位为true的情况 public void interrupt()
-
5、几种方法比较:
1、Thread.sleep(long millis)
一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
2、Thread.yield()
暂停当前正在执行的线程,并执行其他线程(可能没有效果)。让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。
3、thread.join()/thread.join(long millis)
join作用是让其他线程变为等待, t1.join();// 让其他线程变为等待,直到当前t1线程执行完毕,才释放。
把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
4、obj.wait()
当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
5、obj.notify()
唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。
-
6、例题:现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
思路:使用join方法实现
代码:
public class JoinThreadDemo02 { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { public void run() { for (int i = 0; i < 20; i++) { System.out.println("t1,i:" + i); } } }); Thread t2 = new Thread(new Runnable() { public void run() { try { t1.join(); } catch (Exception e) { // TODO: handle exception } for (int i = 0; i < 20; i++) { System.out.println("t2,i:" + i); } } }); Thread t3 = new Thread(new Runnable() { public void run() { try { t2.join(); } catch (Exception e) { // TODO: handle exception } for (int i = 0; i < 20; i++) { System.out.println("t3,i:" + i); } } }); t1.start(); t2.start(); t3.start(); } }