基本概念
* 程序、进程、线程的基本概念和关系
* 程序:是计算机指令的集合.程序是一组静态的指令集,不占用系统运行资源,不能被系统调度,也不能作为独立运行的单位,它以文件的形式存储在磁盘上
* 进程:
* 是一个程序在其自身的地址空间中的一次执行活动。比如,打开一个记事本,就是调用了一个进程。进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,一个程序可以对应多个进程,例如著名的浏览器
* 线程: 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
* 为什么要进行多线程编程
* 可以更好的实现并行
* 恰当地使用线程时,可以降低开发和维护的开销,并且能够提高复杂应用的性能。
* CPU在线程之间开关时的开销远比进程要少得多。因开关线程都在同一地址空间内,只需要修改线程控制表或队列,不涉及地址空间和其他工作。
* 创建和撤销线程的开销较之进程要少。
* Java在多线程应用中的优势
* Java 给多线程编程提供了内置的支持
* 多线程操作会增加程序的执行效率。各线程之间切换执行,时间比较短,看似是多线程同时运行,但对于执行者CPU来说,某一个时刻只有一个线程在运行
* Java线程的生命周期
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
* 线程创建与调度
* Thread类
* 线程开发步骤
* 1 定义线程类
public class MThread extends Thread{
@Override
public void run() {
System.out.println("哥们为被执行:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//像main执行想要执行代码
System.out.println("另一个线程执行java代码..."+Thread.currentThread().getName());
}
}
* 2 创建线程对象并启动线程
public class Test1 {
public static void main(String[] args) {
// 默认有个线程
System.out.println("main线程的名字:"+Thread.currentThread().getName());
// 新建状态
MThread mThread=new MThread();
// 就绪状态
//mThread.run();// 直接调用,当成普通类来使用
mThread.start();//等着CPU去调度进入运行状态
//运行状态
//死亡状态
}
* 3 匿名内部类编写
new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}.start();
* Runnable接口
* 直接继承Thread类实现线程的方法存在局限性:由于Java是典型的单亲继承体系,因此一旦类继承Thread之后就不能再继承其他父类,对于一些必须通过继承关系来传播的特性这种方式显然会造成困扰 ,可以通过实现Runnable接口的方式来实现线程
* 线程启动和停止
* 主线程:任何一个Java程序启动时,一个线程立刻运行,它执行main方法,这个线程称为程序的主线程
* 主线程的特殊之处在于: 它是产生其它线程;
* 通常它必须最后结束,因为它要执行其它子线程的关闭工作。
* 任何Java程序都至少有一个线程
* 线程的启动
* 可以使用start方法来启动(调用后并不是立即执行多线程代码,而是使该线程变为就绪状态,什么时候运行是由操作系统调度决定的 )
* 直接调用run方法,不是启动线程的方法,而是正常类型的对象调用跟线程无关。
* 线程停止
* 线程的停止远比线程的启动情况复杂
* 在Thread线程类中提供了stop()方法用于停止一个已经启动的线程,但是它已经被废弃,不建议使用,因为它本质上就是不安全的,它会解锁线程所有已经锁定的监视程序,在这个过程中会出现不确定性,导致这些监视程序监视的对象出现不一致的情况,或者引发死锁
* 如何安全的停止一个正在运行的线程呢?
* 线程对象的run()方法所有代码执行完成后,线程会自然消亡,因此如果需要在运行过程提前停止线程,可以通过改变共享变量值的方法让run()方法执行结束
public class TestStopThread extends Thread{
private boolean flag=true;
@Override
public void run() {
while(flag) {
System.out.println("xxx");
}
}
public void stopThread() {
flag=false;
}
public static void main(String[] args) {
TestStopThread tt=new TestStopThread();
tt.start();
tt.stopThread();
}
}
* 如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?
* 使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码
public class StopSleepThread extends Thread{
private boolean flag=true;
@Override
public void run() {
while(flag) {
try {
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
e.printStackTrace();
flag=false;
}
}
}
public void stopThread() {
flag=false;
}
public static void main(String[] args) {
StopSleepThread tt=new StopSleepThread();
tt.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// tt.stopThread();
tt.interrupt();
}
}
* interrupt()方法并不能阻断I/O阻塞或线程同步引起的线程阻塞
* 关闭底层I/O通道,人为引发异常从而进行共享变量重新赋值而跳出线程的run()方法
* nio支持非阻塞式的事件驱动读取操作,在这种模式下,不需要关闭底层资源即可通过interrupt()方法直接中断其等待操作
* 线程不同状态之间的转换
各种状态一目了然,值得一提的是"blocked"这个状态:
线程在Running的过程中可能会遇到阻塞(Blocked)情况
调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。
此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。
* 守护线程
* Java中将线程划分为了两类
* 守护线程 (Daemon Thread)
* 用户线程 (User Thread)
* 守护线程 (Daemon Thread)
* 是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止
* 用户线程
* 用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的退出:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了
public class DaemonThread extends Thread{
@Override
public void run() {
for(;;) {
}
}
public static void main(String[] args) {
DaemonThread daemonThread=new DaemonThread();
daemonThread.setDaemon(true);
daemonThread.start();
}
}
* 线程类重要方法的作用
* setPriority()方法
* Java线程可以有优先级的设定,高优先级的线程比低优先级的线程有更高的几率得到执行(注意是更高的几率,而不是优先级高的一定有优势,但是优先级在某一些线程调度方法中有特定的作用)
* Java线程的优先级是一个整数,其取值范围是1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
* public static native void yield();
* 暂停当前正在执行的线程对象,并执行其他线程。
* yield() 方法只是使当前线程重新回到就绪可执行状态,所以执行yield()线程有可能在进入到就绪状态后马上又被执行,只能使相同或更高优先级的线程有执行的机会
* 不会释放锁资源
* public static native void sleep(long millis);
* 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
* Java并不保证线程在阻塞给定的时间后能够马上执行(事实上这几乎是不可能的事情),在阻塞时间到了之后,线程进入就绪状态,继续执行的时机取决于Java虚拟机的线程调度机制,唯一能够确定的是,线程中断执行的时间是大于等于给定的阻塞时长的,因此不要将sleep用作精确度要求非常高的定时任务调度
* 不会释放锁资源
public class ThreadYield implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// Thread.yield();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
Thread t1=new Thread(new ThreadYield());
Thread t2=new Thread(new ThreadYield());
t1.start();
t2.start();
}
}
* join(long millisec)方法
* 等待该线程终止的时间最长为 millis 毫秒。
* 默认是0毫秒:代表forever
* 通常用于在main()主线程内,等待其它线程完成再结束main()主线程
import java.util.Date;
public class JoinTestThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" begin: "+new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" end: "+new Date());
}
public static void main(String[] args) {
Thread t1=new Thread(new JoinTestThread());
Thread t2=new Thread(new JoinTestThread());
t1.start();
t2.start();
System.out.println("main thread go out ");
}
}
import java.util.Date;
public class JoinTestThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" begin: "+new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" end: "+new Date());
}
public static void main(String[] args) {
Thread t1=new Thread(new JoinTestThread());
Thread t2=new Thread(new JoinTestThread());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread go out ");
}
}
import java.util.Date;
public class JoinTestThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" begin: "+new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" end: "+new Date());
}
public static void main(String[] args) {
Thread t1=new Thread(new JoinTestThread());
Thread t2=new Thread(new JoinTestThread());
t1.start();
t2.start();
try {
t1.join(1000);
t2.join(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread go out ");
}
}
* 线程同步
* https://www.cnblogs.com/XHJT/p/3897440.html
* 用线程同步解决问题
* 线程间通讯
* wait/notify
* https://www.cnblogs.com/hapjin/p/5492619.html
* 线程池
* https://blog.csdn.net/liuchuanhong1/article/details/52042182
* 定时任务调度
* Timer timer,线程池,quart
* http://blog.51cto.com/zhangfengzhe/2064092
* 参考链接
* http://www.runoob.com/java/java-multithreading.html