概念
程序:指令集,静态概念
进程: 操作系统 ,调度程序 ,动态概念,占用特定的地址空间,由cpu data code 组成,缺点占内存,CPU负担重。pid
线程:又称为轻量级进程,是进程中的一个单一的连续控 流程。线程之间相互独立。一个进程可拥有多个并行的线程。线程间的通行时在同一地址空间上进行的 ,所以不需要额外的通行机制,可以共享变量,也会造成并发问题。是进程内多条执行路径。(真正的多线程只存在于多cpu机器,单CPU属于模拟多线程,时间片实现(挂起))
进程是资源分配的基本单位,线程是资源调度的基本单位
多线程实现
main方法也是个线程,被称主线程
通过继承Thread类
(1)创建多线程重写run方法(方法体),一切从run开始(alt+shift+s 重写父类的方法)
(2)使用多线程:创建子类对象,调用对象 的start方法(start只是加到了线程组里,cpu调用的时候才是真正执行。不是调用run方法,内部会自己调用。调用run方法就是普通的方法调用,只有一条路径)
package com.pku.java.secondstage;
import java.io.IOException;
import java.sql.Time;
class ThreadA extends Thread{
public void run() {
// TODO Auto-generated method stub
try{
for(int i=0;i<=100;i++){
System.out.println("thread A run"+i+"steps");
sleep(100);
}}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
try{
for(int i=0;i<=100;i++){
System.out.println("thread B run"+i+"steps");
sleep(100);
}}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadTest {
public static void main(String args[]){
ThreadA A = new ThreadA();
ThreadB B= new ThreadB();
A.start();
B.start();
for (int i=0;i<=100;i++){
System.out.println("this is the "+i+"step of main thread");
}
}
}
对于继承了Thread的实例对象,只需调用其start方法,之后如何调度由cpu处理。
通过实现Runnable接口
(1)实现runnable接口并重写run方法
(2)启用多线程使用静态代理(代理者为Thread)
- 创建真实角色
- 创建代理角色,添加真实角色的引用
(3) 调用代理的Start方法
好处 避免单继承的局限性,便于共享资源
class hello implements Runnable{
public void run(){
for(int i=0;i<=1000;i++) {
System.out.println("输出中文");
}
}
}
public class DclientTest {
public static void main(String args[]){
hello aHello = new hello();
Thread proxya = new Thread(aHello);
proxya.start();
for(int i=0;i<1000;i++){
System.out.println("print english");
}
}
}
eg:
模拟多个线程抢票
public class Web12306 implements Runnable {
private int num=50;
public void run() {
while(true){
if(num<=0)
break;
System.out.printf(Thread.currentThread().getName()+"抢到了",num--);
System.out.println();
}
}
public static void main(String args[]){
Web12306 test = new Web12306();
Thread aThread = new Thread(test);
Thread bThread = new Thread(test);
Thread cThread = new Thread(test);
aThread.start();
bThread.start();
cThread.start();
}
}
核心方法只有一个,代理可以有多个。代理同时执行即多线程
小结:两种实现多线程的方法
继承Thread+run():通过创建子类对象并调用对象的start()方法
-
实现Runnable接口和run()方法:通过使用静态代理
1.创建真实角色
2.创建代理角色 Thread+引用
3.代理角色.Start()
第二种方法的优点,避免单继承的局限性,便于共享资源
前两种方法只能通过try来声明异常不能抛出,也不能返回值,这时要用到第三种创建线程的方法
通过Callable接口
优点:可以获取返回值
Callable和Future接口
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可以被其他线程执行的任务。
具体实现:
1)创建Callable实现类+重写Call
2)借助执行调度服务ExecutorService,获取Future对象
ExecutorService ser = Executors.newFixedThreadPool(2);//开启线程数
Future <Integer> result = ser.submit(实现类对象); //<返回值类型>
3)获取值
result.get()
4)停止服务
ser.shutdownNow()
Callable和Runnable的不同:
(1)Callable规定的方法是call,而Runnable规定的方法是run()
(2)call()方法可以抛出异常,而而run()方法不能抛出异常
(3)Callable的任务执行后可以返回值,运行Callable任务可以拿到一个Future队 形,而Runnable的任务是不能返回值的,Future对象表示异步计算的结果,通过这个对象可以了解任务的执行情况,或者可以取消任务的执行,还可以获取任务执行的结构
import java.util.FormatFlagsConversionMismatchException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThridRunTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService ser = Executors.newFixedThreadPool(2);
Race tortoise = new Race("tortoise",200);
Race rabit = new Race("rabit",50);
Future <Integer> result1 = ser.submit(tortoise);
Future <Integer> result2 = ser.submit(rabit);
Thread.sleep(2000);// 让线程运行两秒钟停下
tortoise.setFlag(false);
rabit.setFlag(false);
int nums1 = result1.get();
System.out.println("the tortoise has run-->"+nums1);
int nums2 = result2.get();
System.out.println("the rabit has run-->"+nums2);
ser.shutdown();
}
}
class Race implements Callable<Integer>{
private String name;
private long time;
public long getTime() {
return time;
}
private boolean flag =true;
public void setFlag(boolean flag) {
this.flag = flag;
}
private int step =0;
public Race(String name) {
super();
this.name =name;
// TODO Auto-generated constructor stub
}
public Race(String name,long time) {
super();
this.name =name;
this.time =time;
// TODO Auto-generated constructor stub
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
while(flag){
Thread.sleep(time);
step++;
}
return step;
}
public String getName(){
return this.name;
}
}
output:
the tortoise has run-->10
the rabit has run-->40
线程的状态
线程状态:
新生状态:
用new关键字建立一个线程对象后, 该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间, 通过调用start进入就绪状态
就绪状态:
处于就绪状态线程具备了运行条件, 但还没分配到CPU, 处于线程就绪队列, 等待系统为其分配CPU。当系统选定一个等待执行的线程后, 它就会从就绪状态进入执行状态, 该动作称之为“cpu调度” 。
运行状态:
在运行状态的线程执行自己的run方法中代码, 直到等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束, 就会被系统给换下来回到等待执行状态。
阻塞状态:
处于运行状态的线程在某些情况下, 如执行了sleep(睡眠) 方法, 或等待I/O设备等资源, 将让出CPU并暂时停止自己的运行, 进入阻塞状态。在阻塞状态的线程不能进入就绪队列。 只有当引起阻塞的原因消除时, 如睡眠时间已到, 或等待的I/O设备空闲下来, 线程便转入就绪状态, 重新到就绪队列中排队等待, 被系统选中后从原来停止的位置开始继续运行。
死亡状态:
死亡状态是线程生命周期中的最后一个阶段。 线程死亡的原因有三个。 一个是正常运行的线程完成了它的全部工作; 另一个是线程被强制性地终止, 如通过执行stop方法来终止一个线程[不推荐使用】 , 三是线程抛出未捕获的异常
停止线程
1.自然终止:线程体执行完毕
2.外部干涉:
- 线程类中定义线程体使用的标志
- 线程体中使用该标志
- 提供对外的方法改变该标识
public class ThreadStopTest {
public static void main(String[] args) throws InterruptedException {
Study aStudy = new Study();
new Thread(aStudy).start();
Thread.sleep(2000);
aStudy.setFlag(false);
}
}
class Study implements Runnable{
// public Study() {
// // TODO Auto-generated constructor stub
// }
private boolean flag =true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(flag){
System.out.println("the thrad is running");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
线程阻塞
两种方法:
(1)join:合并线程
public class ZuseTest implements Runnable{
public static void main(String args[]) throws InterruptedException{
ZuseTest aTest = new ZuseTest();
Thread aThread = new Thread(aTest);
aThread.start();
int i=0;
while(true){
System.out.println(i);
i++;
if (i==50) aThread.join();
Thread.sleep(300);
}
}
public void run() {
for (int i=0;i<=100;i++){
System.out.println("A");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
开始时主线程和子线程并行执行,等到i=50的时候,执行athread.join(),主线程(main)被阻塞,直到aThread线程执行完毕再执行主线程。
(2)yield:暂停线程,暂停当前正在执行的执行对象,暂停自己(是个静态方法)
写在哪个线程里面就暂停谁
public class YieldTest implements Runnable{
@Override
public void run() {
for (int i=0;i<=1000;i++){
System.out.println("this is Thread"+i);
}
}
public static void main(String[] args) {
Thread aThread = new Thread(new YieldTest());
aThread.start();
int i=1;
while(true){
if(i >500) break;
i++;
System.out.println("main ==>"+i);
if(i % 20 ==0){
Thread.yield();
}
}
}
}
被暂停的线程可能会被cpu再次调度
(3)sleep(静态方法)
休眠,暂停当前进程,休眠的时候不会释放锁。
作用:
- 计时
- 模拟网络延迟