一.进程与线程的概念
进程
1.当一个程序被运行,就开启了一个进程, 比如启动了qq,word
2.程序由指令和数据组成,指令要运行,数据要加载,指令被cpu加载运行,数据被加载到内存, 指令运行时可由cpu调度硬盘、网络等设备
线程
1.CPU调度和分派的基本单位
2.执行运算的最小单元,可完成一个独立的顺序控制流程
3.如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称为多线程
4.多个线程交替占用CPU资源,并非真正的并行执行
主线程
main()方法即为主线程入口,是产生其他子线程的线程
main()方法必须最后完成执行。因为它执行各种关闭动作
public class MainThread {
public static void main(String[] args) {
//获取当前的线程对象
Thread thread=Thread.currentThread();
System.out.println("当前线程名称是:"+thread.getName());
thread.setName("New main");
System.out.println("当前线程名称是:"+thread.getName());
}
}
二.java中创建线程的方式
1.扩展java.lang.Thread类
这里继承Thread类的方法是比较常用的一种,如果说你只是想起一条线程。没有什么其它特殊的要求,那么可以使用Thread,下面来看一个简单的实例:
//创建并启动线程
public class MyThread extends Thread{
public void run(){
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//创建线程对象并启动
MyThread myThread=new MyThread();
MyThread myThread2=new MyThread();
//启动线程
myThread.start();
myThread2.start();
//是否可以调用run()方法启动呢?
// myThread.run();//只有主线程一个执行路径;依次调用了两次run()方法
// myThread2.run();//run()方法被当成main()方法中一个普通方法执行,失去了线程的意义
由此可以看出,所谓的多线程其实是多个线程交替执行,并不是真正的并行;
线程每次执行的时常由CPU分配的时间片长度决定;
不可以直接调用run()方法启动线程:因为run()方法会被当成main()方法中一个普通方法执行,失去了线程的意义。如下图所示
2.实现java.lang.Runnable接口
Thread2类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。
public class MyThread2 implements Runnable{
public void run(){
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread2 {
public static void main(String[] args) {
//创建线程对象
Runnable runnable=new MyThread2();//多态,父类引用指向子类对象
Thread thread=new Thread(runnable,"mythread1");
Thread thread2=new Thread(runnable,"mythread2");
//线程调度:设置线程优先级
thread2.setPriority(10);
thread.setPriority(1);
//启动线程
thread.start();
thread2.start();
}
}
3.Thread和Runnable的区别
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
三.线程中状态转换
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
public class MyThread3 implements Runnable {
public void run(){
System.out.println("线程正在运行!处于运行状态!");
try {
//线程休眠5000ms
System.out.println("线程开始休眠,处于阻塞状态!");
Thread.sleep(5000);
System.out.println("线程休眠结束,阻塞状态结束,再次进入就绪状态!");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("线程中断!");
}
}
}
public class TestThread3 {
public static void main(String[] args) {
Runnable runnable = new MyThread3();
Thread t=new Thread(runnable,"线程状态1");
System.out.println("线程处于创建状态!");
t.start();
System.out.println("线程就绪!");
}
}
四.线程调度
1、调整线程优先级:
Java线程有优先级,优先级高的线程会获得较多的运行机会。
Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORIT——线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY—— 线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY——分配给线程的默认优先级,取值为5。
public class TestThread2 {
public static void main(String[] args) {
//创建线程对象
Runnable runnable=new MyThread2();//多态,父类引用指向子类对象
Thread thread=new Thread(runnable,"mythread1");
Thread thread2=new Thread(runnable,"mythread2");
//线程调度:设置线程优先级
thread2.setPriority(10);
thread.setPriority(1);
//启动线程
thread.start();
thread2.start();
}
}
2、线程睡眠:
Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
public class Wait {
public static void bySec(long s){ //s:休眠时间次数
for (long i = 1; i <= s; i++) {
System.out.println(i+"秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println("********线程开始休眠********");
Wait.bySec(5);
System.out.println("********线程休眠结束********");
}
}
4、线程让步:
Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。 允许其他具有相同优先级的线程获得运行机会,该线程处于就绪状态,而不是阻塞状态,只是提供一种可能,但是不能保证一定产生礼让!!!!
public class MyThreadTest2 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 4; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
if(i==3){
//线程礼让,线程调度:yield()
System.out.println("线程礼让:");
Thread.yield();
}
}
}
}
public class Test2 {
public static void main(String[] args) {
//创建两个线程对象
Runnable runnable=new MyThreadTest2();
Thread t1=new Thread(runnable,"线程A");
Thread t2=new Thread(runnable,"线程B");
t1.start();
t2.start();
}
}
5、线程加入:
join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
public class Test {
public static void main(String[] args) {
//创建子线程对象t
Runnable runnable=new MyThreadTest();
Thread t=new Thread(runnable,"myThread");
t.start();
//主线程main做的事情
for (int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
//当i=5时,强制把t线程加入执行
//线程调度:join()---等待t线程执行结束后,main主线程再继续执行
if(i ==5){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class MyThreadTest implements Runnable{
public void run(){
for (int i = 1; i <= 30; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}