各自努力,最高处见!加油!
线程(一)
一、程序(program)
程序是为了完成任务、用某种语言编写的一组指令的集合。
二、进程
- 进程是指运行中的程序,比如我们使用qq,就启动了一个线程,系统会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
- 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身产生、存在和消亡的过程
三、线程
(1)线程相关概念
- 线程是由进程创建的,是进程的一个实体
- 一个进程可以拥有多个线程
- 如:一个迅雷下载窗口中有多个线程在为我们下载几个不同的任务。
- 单线程:同一个时刻,只允许执行一个线程
- 多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件。
- 并发:同一时刻,多个任务交替进行,造成一种“貌似同时”的错觉,简单的说,单核CPU实现的多任务就是并发。
- 并行:同一时刻,多个任务同时执行。多核CPU可以实现并行。
(2)线程的基本使用:创建线程的两种方式
- 继承Thread类,重写run方法。
- 实现Runnable接口,重写run方法。
- Thread类中的run方法是实现了Runnable接口的run方法。
①继承Thread类,重写run方法。
示例代码:
public class Thread01 {
public static void main(String[] args) {
Cat cat=new Cat();
cat.start();//启动线程,会自动调用Cat类中的run方法
//说明:当main线程启动一个子线程Tread-0,主线程不会阻塞,会继续执行
System.out.println("主程序继续执行:"+Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println("主线程:"+i);
Thread.sleep(1000);
}
}
}
//当一个类继承了Thread类,该类就可以当做线程使用
class Cat extends Thread{
int time;
@Override
public void run() {
//重写run方法,写上业务代码
while(true) {
System.out.println("快乐" + (++time));
try {//Ctrl+alt+t
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
疑问解答:为什么不直接在main方法中直接调用run方法?
解:直接在main方法中调用run方法的话,run方法是在main线程中执行,达不到多线程的效果。
在start方法的内部,调用的是start0()方法。start0方法是本地方法,由JVM调用,底层是C/C++实现。==>真正实现多线程的效果是start0(),而不是run方法。
start()方法调用start0()方法后,该线程并不一定会马上执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU同一调度。
②实现Runnable接口,重写run方法。
示例代码:
public class Thread023 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog);//Thread类的静态代理模式
thread.start();
}
}
class Dog implements Runnable{
int count=0;
@Override
public void run() {
while(count<80){
System.out.println("小狗"+(++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在代理模式中,最核心的还是调用了start0()方法,凡是多线程都离不开start0方法的调用。
多个线程:
(3)继承Thread与实现Runnable接口两种方法实现多线程的区别
- 从java的设计来看,两种方法没有本质区别,从jdk帮助文档来看,Thread类本省就实现了Runnable接口。start()–>start0()
- 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制。
T3 t3=new T3("Hello");
Thread thread01=new Thread(t3);
Thread thread02=new Thread(t3);
thread01.start();
thread02.start();
System.out.println("主线程执行完毕");
(4)多线程售票系统
//三个窗口同时售票100张
public class SellTicket {
public static void main(String[] args) {
// SellTicket01 sellTicket01 = new SellTicket01();
// SellTicket01 sellTicket02 = new SellTicket01();
// SellTicket01 sellTicket03 = new SellTicket01();
// sellTicket01.start();
// sellTicket02.start();
// sellTicket03.start();
SellTicket02 sellTicket04=new SellTicket02();
SellTicket02 sellTicket05=new SellTicket02();
SellTicket02 sellTicket06=new SellTicket02();
Thread thread1 = new Thread(sellTicket04);
Thread thread2 = new Thread(sellTicket05);
Thread thread3 = new Thread(sellTicket06);
thread1.start();
thread2.start();
thread3.start();
}
}
//使用继承Thread类的方式
class SellTicket01 extends Thread{
private static int ticketNum=100;
@Override
public void run() {
while (true){
if(ticketNum<=0){
System.out.println("售票结束");
break;
}
// ticketNum--;
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数:"+(--ticketNum));
//将--的位置提前可以初步解决超卖的问题:即在剩余票数不足的情况下,三个线程同时售票导致出现负票数的情况。
}
}
}
//采用实现Runnable接口的方法
class SellTicket02 implements Runnable{
private static int ticketNum=100;
@Override
public void run() {
while (true){
if(ticketNum<=0){
System.out.println("售票结束");
break;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数:"+(--ticketNum));
}
}
}
(5)线程终止
- 当线程完成任务后,会自动退出。
- 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。
代码示例:启动一个线程t,要求在main线程中停止线程t,编程实现。
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();
//让主线程休息10s后通知线程退出
Thread.sleep(10*1000);
t.setLoop(false);
}
}
class T extends Thread{
private int count=0;
private boolean loop=true;
@Override
public void run() {
while (loop){
System.out.println("程序运行中。。。"+(++count));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}