多线程
使用多线程可以让程序充分利用CPU资源,提高CPU的使用效率,从而解决高并发带来的负载均衡问题。
优点:
-
CPU资源得到更合理的利用。
-
程序设计更加简洁。
-
程序响应更快,运行效率更高。
缺点:
-
需要更多的内存空间来支持多线程。
-
多线程并行访问的情况可能会影响数据的准确性。
-
数据被多线程共享,可能出现死锁的情况。
重点:线程状态,线程调度,线程同步,高并发与线程池,实战为主。
进程
是指计算机在某个数据集合上进行的一次运行活动,是系统进行资源分配调度的基本单位。简单理解,进程就是计算机正在运行的一个具体的应用程序。
线程是进程的基本单位,程序执行的最小单元,为独立完成程序中的某个功能而存在,一个进程是由一个或者多个线程组成的。
进程和线程的区别
进程在运行时拥有独立的存储空间,每个进程所占用的内存都是独立的,互不干扰。
多个线程是共享内存空间的,每个线程都有其执行的入口和出口,但是线程不能独立执行,必须依赖于进程,由进程提供多线程的执行控制。
多线程:在一个进程中,多个线程同时执行。
### Java中线程的使用
-
继承Thread类
-
创建自定义类并继承Thread
-
重写Thread的run方法,并编写该线程的业务逻辑代码
1.自定义一个线程类,继承Thread类,重写父类的run方法,完成自定义线程的具体的业务逻辑。
定义好了⼀个线程类之后,我们就可以通过该类来实例化对象,对象就可以描述⼀个线程。实例化该对象之后,必须通过调⽤start()来开启该线程,这样该线程才会和其他线程来抢占CPU资源,不能调⽤run()⽅法,调⽤run()相当于普通的实例对象⽅法调⽤,并不是多线程。
使用:实例化自定义线程类的对象,该对象就是一个线程对象,然后启动线程即可。
public class MyThread extends Thread{
//自定义线程的具体的业务逻辑
public void run(){
dosomething...
}
}
//使用
MyThread myThread = MyThread();
myThread.start();
2.自定义一个类,实现Runnable接口,实现接口的run方法,完成自定义线程的具体业务逻辑。
使用:实例化实现类的对象,但是它并不是一个线程对象,它只是实现了线程要执行的业务逻辑,还需要实例化一个线程对(Thread),将实现类对象传给线程对象,就会覆盖线程对象继承自父类的run方法,完成自定义的业务逻辑。
//自定义Runnable实现类
public class MyRunnable implements Runnable{
//自定义线程要执行的具体的业务逻辑
public void run(){
dosomething...
}
}
//使用
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 100; i < 200; i++) {
System.out.println(i);
}
}
}).start();
线程的状态
1.创建状态:一个线程对象被实例化之后。
2.就绪状态:创建好的线程对象调用了start方法启动之后。
3.运行状态:当前线程对象获取了CPU资源,可以执行其业务逻辑代码(run方法的代码)。
4.阻塞状态:运行状态的线程进行了休眠或其他操作导致线程暂停执行。
5.死亡状态:线程执行完毕或者因为异常退出。
线程礼让与线程休眠的区别:休眠指从运行状态进入阻塞状态----礼让即从运行状态或就绪状态来到阻塞状态
线程调度
- 线程休眠
休眠指让当前线程暂停执行一段时间,从运行状态进入阻塞状态,将CPU资源让给其他线程的一种调度方法,通过sleep方法来实现,sleep(long millis)是java.lang.Thread类中定义的方法,使用时需要指定当前线程休眠的时间,单位为毫秒(1000毫秒=1秒)。
package com.m.test;
public class Class02 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
try {
//Thread.sleep(3000);
myThread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.start();
}
}
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(i == 5) {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(i);
}
}
}
mian线程进行休眠操作
package com.m.test;
public class Class02 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
if (i == 5) {
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(i);
}
}
}
- 线程合并
合并的意思是将指定的某个线程合并到当前线程中,将原本两个交替执行的线程改为顺序执行,即一个线程执行完毕之后再来执行第二个线程,通过调用线程对象的join方法来实现合并。
具体如何来实现合并?谁为主谁为从?假设有两个线程:线程a,线程b。
线程a在执行到某个时间点的时候调用了线程b的join方法,则表示从当前时间点开始CPU资源被线程b独占,线程a进行阻塞状态,直到线程b执行完毕,线程a重新进入就绪状态,等待获取CPU资源进入运行状态继续执行。
package com.m.test;
public class Class01 {
public static void main(String[] args) {
JoinRunnable joinRunnable = new JoinRunnable();
Thread thread = new Thread(joinRunnable);
thread.start();
for (int i = 0; i < 100; i++) {
if(i == 10) {
try {
// thread.join(3000);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(i+"Test---------------------");
}
}
}
public class JoinRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i+"+++++++++++++++++++JoinRunnable");
}
}
}
注意:同样是完成线程合并的操作,join()和join(long millis)是有区别的,join()表示被调用线程执行完成之后才能释放CPU资源,让其他线程来执行,join(long millis)则表示被调用的线程执行millis毫秒之后,无论其是否执行完毕,其他线程都可以和它来争夺CPU资源。
- 线程礼让
线程礼让是指在某个特定的时间点,让当前线程暂停抢占CPU资源的行为,即从运行状态或就绪状态来到阻塞状态,从而将CPU资源让给其他线程来使用。假如有线程a和线程b在交替执行,某个时间点线程a作出了礼让,所以在这个时间点线程b就拥有了CPU资源,执行其业务逻辑,但不是说线程a会一直暂停争夺,线程a只是在特定的时间节点进行礼让,一旦过了这个时间节点,线程a在此进入就绪状态,和线程b来争夺CPU资源。
通过调用线程对象的yield方法来完成礼让。
- 线程中断
比如:下载资源,取消下载。
Java中实现线程中断机制有如下几个方法:
public void stop() 粗暴的方式
public void interrupt()
public boolean isInterrupt()
stop()方法在新版本的JDK已经不推荐使用了,interrupt是一个实例方法,当一个线程对象调用该方法时,表示中断当前线程对象。
每一个线程都有一个标志位来标识当前线程对象是否为中断状态,isInterrupt()方法就是用来获取当前线程对象的标志位的,true表示清除了标志位,当前线程对象已经中断,false表示没有清除标志位,当前线程对象没有中断。
总结:
线程调度:
1、线程休眠(sleep):让当前线程对象暂停执行,从运行状态进入阻塞状态,将
CPU资源让给其他线程的一种调度方式。
2、线程合并(join):线程甲在某个时刻调用了线程乙的join,从这一时刻开始,
CPU资源完全被线程乙占用,直到线程乙的任务执行完毕,将CPU资源释放,
线程甲才能继续执行。
3、线程礼让(yield):让当前线程在某一个时刻让出CPU资源,仅仅是在这一时刻进行
礼让,在下一个时刻会继续争夺CPU资源。
4、线程中断(interrupt):
线程中断有三种方式:
1.线程的任务执行完毕,自动关闭。
2.线程执行过程中,因为抛出异常导致线程中断。
3.根据需求手动进行中断。
备注:最近来手感,写了个类似Tomcat服务
github地址:https://github.com/cnamep001/my_tomcat.git