Java多线程---线程的创建和使用
主要内容
程序、进程、线程的概念
Java中多线程的创建和使用
1、继承Thread类与Thread类的主要方法
2、实现Runable接口
3、线程的调度与设置优先级
一、程序、进程、线程的概念
1、程序(program):可以理解为一段静态的代码,静态对象。是为了完成特定任务、用某种语言编写的一组指令的集合。
2、进程(process):它是内存中的一段独立的空间,可以负责当前应用程序的运行。当前这个进程负责调度当前程序中的所有运行细节。
进程是一个动态过程,有它自己的产生、存在和消亡的过程。
3、线程(thread):它是位于进程中,负责当前进程中的某个具备独立运行资格的空间。
区别:进程是负责整个程序的运行,而线程是程序中具体的某个独立功能的运行。一个进程中至少应该有一个线程。
4、多线程:
在一个进程中,同时开启多个线程,让多个线程同时去完成某些任务(功能)。
(比如后台服务系统,就可以用多个线程同时响应多个客户的请求)
多线程的目的:提高程序的运行效率。
多线程运行原理:
cpu在线程中做时间片的切换。其实真正电脑中的程序的运行不是同时在运行的。
CPU负责程序的运行,而CPU在运行程序的过程中某个时刻点上,它其实只能运行一个程序。而不是多个程序。
而CPU它可以在多个程序之间进行高速的切换。而切换频率和速度太快,导致人的肉眼看不到。
每个程序就是进程, 而每个进程中会有多个线程,而CPU是在这些线程之间进行切换。
了解了CPU对一个任务的执行过程,我们就必须知道,多线程可以提高程序的运行效率,但不能无限制的开线程。
二、Java中多线程的创建和使用
1、继承Thread类
(1)实例:
package com.TestThread; /* * 创建一个子线程,完成1-100之间自然数的输出,同样,主线程也执行这个操作 * 创建多线程的第一种方式:继承java.lang.Thread类 */ //1、创建一个继承于Thread的子类 class SubThread extends Thread{ //2、重写Thread类的run()方法,方法内实现此子线程要完成的功能 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) { //3、创建一个子类的对象 SubThread st1=new SubThread(); SubThread st2=new SubThread(); //4、调用线程的start()方法:启动此线程,调用相应的run()方法 //一个线程只能够执行一次start()方法 //不能通过Thread实现类对象的run()去启动一个线程 st1.start(); //st1.start() //st1.run() st2.start(); for(int i=1;i<=100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
(2)Thread的常用方法
实例:
package com.TestThread; /* * Thread的常用方法 * 1.start():启动线程并执行相应的run()方法 * 2.run():子线程要执行的代码放入run()方法 * 3.currentThread():静态的,调取当前的线程 * 4.getName():获取此线程的名字 * 5.setName():设置此线程 的名字 * 6.yield():调用此方法的线程释放当前CPU的执行权 * 7.join():在A线程中调用B线程的join()方法,表示当执行到此方法,A线程停止执行,直至B线程执行完成,A线程再接着join()之后的代码执行 * 8.isAlive():判断当前线程是否还存活 * 9.sleep(long L):显式地让当前线程睡眠L毫秒 * 10.线程通信:wait() notify() notifyAll() * 设置线程的优先级:时间片,抢占式 * getPriority():返回线程优先值 * setPriority(int new Priority):改变线程的优先级 */ class SubThread1 extends Thread{ public void run(){ for(int i=1;i<=100;i++){ /*try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ System.out.println(Thread.currentThread().getName()+":"+i); } } } public class TestThread1 { public static void main(String[] args) { SubThread1 st1=new SubThread1(); SubThread1 st2=new SubThread1(); st1.setName("子线程1"); st1.start(); st2.setName("子线程2"); st2.start(); Thread.currentThread().setName("=========主线程"); for(int i=1;i<=100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); /*if(i%10==0){ Thread.currentThread().yield(); }*/ if(i == 20){ try { st1.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } System.out.println(st1.isAlive()); } }
(3)设置线程的优先级:时间片,抢占式
getPriority():返回线程优先值
setPriority(int newPriority):改变线程的优先级
MAX_PRIORITY(10) MIN_PRIORITY(1) NORM_PRIORITY(5)
(4)简单练习
模拟火车站售票窗口,开启三个窗口售票,总票数为100
(此程序存在线程安全问题,打印车票时可能出现重复车票以及错票,这只是一个简单的实现,没考虑线程安全问题)
package com.TestThread; //模拟火车站售票窗口,开启三个窗口售票,总票数为100 //方式一:继承Thread类 class Window extends Thread{ static int ticket=100; public void run(){ while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"售出的票号为:"+ticket--); }else{ break; } } } } public class TestWindow { public static void main(String[] args) { Window w1=new Window(); Window w2=new Window(); Window w3=new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w3.setName("窗口3"); w1.start(); w2.start(); w3.start(); } }
2、实现Runable接口
(1)实例:
package com.TestThread; //方式二:实现Runnable接口 //1、创建一个实现了Runnable接口的类 class PrintNum1 implements Runnable{ //2、实现接口的抽象方法 public void run() { //子类执行的代码 for(int i=1;i<=100;i++){ if(i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class TestRunnable { public static void main(String[] args) { //3、创建一个Runnable接口实现类的对象 PrintNum1 p=new PrintNum1(); //4、将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程 Thread t1=new Thread(p); //5、调用start()方法,启动线程并执行run()方法 t1.start();//启动线程,执行Thread对象生成时构造器形参的对象的run()方法 //再创建一个线程 Thread t2=new Thread(p); t2.start(); } }
(2)简单练习
模拟火车站售票窗口,开启三个窗口售票,总票数为100
(此程序存在线程安全问题,打印车票时可能出现重复车票以及错票,这只是一个简单的实现,没考虑线程安全问题)
package com.TestThread; //使用实现Runnable接口方式售票 class Window1 implements Runnable{ int ticket=100; public void run() { while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"售出的票号为:"+ticket--); }else{ break; } } } } public class TestWindow2 { public static void main(String[] args) { Window1 w=new Window1(); Thread t1=new Thread(w); Thread t2=new Thread(w); Thread t3=new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
3、两种方式的对比
(1)联系:Thread类是实现Runnable接口的。
(2)实现Runnable接口的方式优于继承Thread类的方式
理由:a.避免了Java单继承的局限性
b.如果多个线程要操作同一份资源(或数据),更适合使用实现的方式。
作者: Aquamarine
链接:https://www.imooc.com/article/22453
来源:慕课网