目录
程序--进程--线程
程序:
进程:
线程:
进程和线程的关系
创建线程
单线程模式
代码
图解
运行
代码
运行
创建线程
方式一
代码
运行
方式二
代码
运行
总结:
Thread类中常用的方法
1、
代码
运行
2、
代码
运行
3、
代码
运行
4、
代码
运行
5、
代码
运行
编辑
线程优先级
操作系统线程任务调度算法
线程状态
新建
就绪
运行
阻塞
死亡
守护线程
代码
运行
多线程的概念
何时需要多线程
多线程的优点
多线程的缺点
线程同步
多线程同步
加锁方式1:
Thread类
修饰代码块
代码
运行
修饰方法
代码
运行
Runnable类
修饰代码块
代码
运行
修饰方法
代码
运行
加锁方式2:
代码
运行
synchronized 与 ReentrantLock区别
相同点:
不同点:
线程通信
代码
运行
新增创建线程方式
代码
运行
程序--进程--线程
程序:
为解决某种问题,通过计算机语言编写的一系列指令(代码)的集合。
线程中的程序特指的是静态的,安装在硬盘上的代码集合。
进程:
运行中的程序(被加载到内存中),是操作系统进行资源分配的最小单位。
线程:
进程可以进一步细化为线程,是进程内最小的执行单位(具体要做的事情),是cpu进行任务调度的最小单位 。
运行中的QQ就是一个进程,操作系统会为这个进程分配内存资源,一个聊天窗口就认为是一个线程,这多个聊天窗口可以同时被cpu执行,但是这些聊天窗口属于进程,线程是属于进程的。
早期没有线程,早期cpu在执行时是以进程为执行单位的,进程单位还是比较大的,当一个进程运行时,其他的进程就不能执行,所以后来,将进程中的多个任务,细化为线程。cpu执行单位,也从进程转为更小的线程。
进程和线程的关系
1、一个进程可以包含多个线程;
2、一个线程只能隶属于一个进程,线程不能脱离进程存在;
3、一个进行更中至少有一个线程(即主线程)java中的main方法,就是用来启动主线程;
4、在主线程可以创建并启动其他线程;
5、所以线程都共享进程的内存资源。
创建线程
单线程模式
代码
package com.ffyc.javathread.demo1;public class Demo {public static void main(String[] args) {for (int i = 0; i < 10; i++) {System.out.println("循环1:" + i);}for (int i = 0; i < 10; i++) {System.out.println("循环2:" + i);}}
}
图解
运行
代码
package com.ffyc.javathread.demo1;public class Demo2 {public static void main(String[] args) {for (int i = 0; i < 10; i++) {System.out.println("循环1:" + i);}test();System.out.println("最后的代码");}public static void test(){for (int i = 0; i < 10; i++) {System.out.println("循环2:" + i);}}}
运行
需求:想在java程序中有几件不相关的事情同时有机会进行进行。
可以在java中创建线程,把一些要执行的任务放在线程中执行,这样的话,都拥有让cpu执行的权力
创建线程
方式一
MyThread 继承 Thread(线程)
重写Thread类中run()方法,在run()方法中来编写我们需要执行的任务代码
调用start()启动线程
代码
package com.ffyc.javathread.demo1;
public class MyThread extends Thread{//run方法是线程执行任务的方法@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println("MyThread"+i);}}
}
package com.ffyc.javathread.demo1;public class Test {public static void main(String[] args) {MyThread myThread = new MyThread();/*//切记不能直接调用run(),普通方法调用,并不是启动线程myThread.run();*///启动线程,启动线程后,并不是会立即执行的,需要操作系统的调度myThread.start();for (int i = 0; i < 10000; i++) {System.out.println("Test"+i);}}
}
运行
方式二
创建一个任务,实现Runnable接口,把这个类不能称为线程,是一个任务类
重写Runnable接口中的run方法
代码
package com.ffyc.javathread.demo2;
public class MyTask implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println("MyTask:"+i);}}
}
main方法是用来启动java主线程的
package com.ffyc.javathread.demo2;public class Test { public static void main(String[] args) {//创建一个任务的对象MyTask myTask = new MyTask();//真正意义上的创建了一个线程Thread thread = new Thread(myTask);//启动线程,去执行mytask任务thread.start();for (int i = 0; i < 10000; i++) {System.out.println("main:"+i);}}
}
运行
总结:
两种方法后期使用率上,第二种相对更高一些
1、避免了单一继承的局限性,因为java是单继承的 ,继承了Thread类,就不能继承其他类
2、更适合多线程共享同一份资源的场景
Thread类中常用的方法
Thread类表示线程,提供了很多的方法,来对线程进行控制。
1、
run();线程要执行的任务在run方法中进行定义
start();启动java线程的
构造方法
new Thread(Runnable runnable);接收一个任务对象
代码
package com.ffyc.javathread.demo1;
public class MyThread extends Thread{ @Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println("MyThread"+i);}}
}
package com.ffyc.javathread.demo1;public class Test {public static void main(String[] args) {MyThread myThread = new MyThread(); myThread.start();for (int i = 0; i < 10000; i++) {System.out.println("Test"+i);}}
}
运行
2、
new Thread(Runnable runnable,String name);接收一个任务对象,并为线程设置名字
setName("我的线程1");为线程设置名字
String getName();获得线程的名字
Thread.currentThread();在任务中获得当前正在执行的线程
代码
package com.ffyc.javathread.demo2;public class MyTask_Name implements Runnable{@Overridepublic void run() {//在任务中,我想知道当前是那个线程正在执行//在任务中,通过currentThread()获得当前正在执行的线程Thread thread = Thread.currentThread();System.out.println(thread.getName());}
}
package com.ffyc.javathread.demo2;public class Test_Name {public static void main(String[] args) {MyTask_Name myTask_name = new MyTask_Name();Thread thread1 = new Thread(myTask_name,"线程1");thread1.setName("我的线程1");thread1.start();//又创建了一个线程,把同一个任务交给线程去执行Thread thread2 = new Thread(myTask_name);thread2.start();}
}
运行
3、
setPriority(int p);设置优先级 1-10之间 默认是5
getPriority();获得优先级
代码
package com.ffyc.javathread.demo3;public class MyTask implements Runnable{@Overridepublic void run() {//获得当前正在执行对象Thread thread = Thread.currentThread();//获得线程优先级System.out.println(thread.getName()+":"+thread.getPriority());}
}
package com.ffyc.javathread.demo3;public class Test {public static void main(String[] args) {MyTask myTask = new MyTask();Thread t1 = new Thread(myTask,"线程1");Thread t2 = new Thread(myTask,"线程2");//设置线程优先级 1-10之间 默认是5t1.setPriority(8);t2.setPriority(2);t1.start();t2.start();System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());}}
运行
4、
sleep(long m);让线程休眠指定的时间 毫秒单位
代码
package com.ffyc.javathread.demo3;public class MyTask implements Runnable{@Overridepublic void run() {//获得当前正在执行对象Thread thread = Thread.currentThread();if (thread.getName().equals("线程1")) {try {//让线程休眠 1000毫秒Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}}//获得线程优先级System.out.println(thread.getName() + ":" + thread.getPriority());}
}
package com.ffyc.javathread.demo3;public class Test {public static void main(String[] args) {MyTask myTask = new MyTask();Thread t1 = new Thread(myTask,"线程1");Thread t2 = new Thread(myTask,"线程2");//设置线程优先级 1-10之间 默认是5t1.setPriority(8);t2.setPriority(2);t1.start();t2.start();System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());}}
运行
5、
join();让其他线程等待当前线程结束
代码
package com.ffyc.javathread.demo3;public class MyTask implements Runnable{@Overridepublic void run() {//获得当前正在执行对象Thread thread = Thread.currentThread(); //获得线程优先级System.out.println(thread.getName() + ":" + thread.getPriority());}
}
package com.ffyc.javathread.demo3;public class Test {public static void main(String[] args) throws InterruptedException {MyTask myTask = new MyTask();Thread t1 = new Thread(myTask,"线程1");Thread t2 = new Thread(myTask,"线程2"); t2.start();//让其他线程等待当前线程结束后再执行t2.join();t1.start();System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());}}
运行
线程优先级
事实上,计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能 执行任务;
优先级较高的线程有更多获得CPU的机会,反之亦然;
优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级 都是5,但是也可以通过setPriority和getPriority方法来设置或返回优 先级;
操作系统线程任务调度算法
先来先服务(FCFS)调度算法
短作业优先(SJF)调度算法
优先级调度算法
时间片轮转调度算法
高响应比优先级调度算法
多级反馈队列调度算法(集合了前几种算法的优点)
线程状态
线程再它的生命周期中会处于不同的状态;
线程生命周期,线程从创建到销毁,期间经历5个状态:
新建
new Thread();处于新建状态,此状态还不能被执行。
调用start()启动线程,让线程进入到就绪状态,
就绪
获得到cpu执行权后,线程进入到cpu执行
运行
运行中的线程可以被切换,又回到就绪状态,也可能因为休眠等原因进入到阻塞状态
阻塞
线程休眠时间到了 回到就绪状态
死亡
当线程中所有的任务执行完了,线程也就自动销毁
守护线程
守护线程也是线程中的一种,区别在于他的结束,如何一个线程是守护线程,那么它会等java中其他线程任务结束后,自动终止。
守护线程是为其他线程提供服务的,例如jvm中的垃圾回收线程,就是一个守护线程
代码
package com.ffyc.javathread.demo4;public class Task implements Runnable{@Overridepublic void run() {while (true){System.out.println("我是守护线程,我为大家服务");}}
}
package com.ffyc.javathread.demo4;public class Test {public static void main(String[] args) {Task task = new Task();Thread thread = new Thread(task);//设置线程为守护线程,设置守护线程必须在启动前设置。thread.setDaemon(true);thread.start();for (int i = 0; i < 10000; i++) {System.out.println("main"+i);}}
}
运行
多线程的概念
多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多 个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行 执行的线程来完成各自的任务。
何时需要多线程
程序需要同时执行两个或多个任务。
程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、 网络操作、搜索等。
多线程的优点
提高程序的处理能力,效率提高了.
提高CPU的利用率.
改善程序结构,将复杂任务分为多个线程,独立运行
多线程的缺点
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多(小问题 可提升硬件设备);
多线程需要协调和管理,所以需要跟踪管理线程,使得cpu开销变大;
线程之间同时对共享资源的访问会相互影响,如果不加以控制会导致数据 出错.
线程同步
确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个 线程获取了这把锁,才有权利访问该共享资源。
多线程同步
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制, 即各线程间要有先来后到;
同步 = 排队 + 锁 一次只能由一个线程访问共享资源
加锁方式1:
多个线程访问同一个共享的数据,如果不加以控制,在理论上就会出现问题
Thread类
使用synchronized关键字
修饰代码块
同步对象要求:
锁对象,必须多个线程对应的是同一个对象,可以是java中任何类的对象
作用:可以记录有没有线程进入到同步代码块中
synchronized (锁对象){
使用锁对象的对象头中的一块空间来记录锁的状态
同步代码块
}
代码
package com.ffyc.javathread.demo5;public class TicketThread extends Thread{String name; //静态变量,在内存中只有一份,两个线程对象共用同一个static int num = 10;//必须确保多个线程对应的是同一个对象即可static String s = new String();@Overridepublic void run() {while (true){synchronized (s){if(num > 0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"买到了:"+num--);}else{break;}}}}
}
package com.ffyc.javathread.demo5;public class Test {public static void main(String[] args) { TicketThread t1 = new TicketThread();t1.setName("窗口1");TicketThread t2 = new TicketThread();t2.setName("窗口2");t1.start();t2.start();}
}
运行
修饰方法
1、锁不需要我们提供了,会默认提供锁对象
2、synchronized如果修饰的是非静态的方法,锁对象是this
synchronized如果修饰的是静态方法,锁对象是类的Class对象
一个类只有一个Class类对象
代码
package com.ffyc.javathread.demo5;public class TicketThread extends Thread{String name;//静态变量,在内存中只有一份,两个线程对象共用同一个static int num = 10; @Overridepublic void run() {while (true){if(num <= 0){break;}TicketThread.printTicket();}} public static synchronized void printTicket(){if(num > 0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"买到了:"+num--);}}
}
package com.ffyc.javathread.demo5;public class Test {public static void main(String[] args) { TicketThread t1 = new TicketThread();t1.setName("窗口1");TicketThread t2 = new TicketThread();t2.setName("窗口2");t1.start();t2.start();}
}
运行
Runnable类
修饰代码块
代码
package com.ffyc.javathread.demo6;import com.ffyc.javathread.demo5.TicketThread;public class Ticket implements Runnable{//对于多个线程来说,票数只有一份int num = 10;@Overridepublic void run() {while (true){//this只有一个synchronized (this){if(num > 0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"买到了:"+num--);}else{break;}}}}
}
package com.ffyc.javathread.demo6;public class Test {public static void main(String[] args) {//创建了一个出票的任务Ticket ticket = new Ticket();Thread t1 = new Thread(ticket,"窗口1");Thread t2 = new Thread(ticket,"窗口2");t1.start();t2.start();}
}
运行
修饰方法
代码
package com.ffyc.javathread.demo6;import com.ffyc.javathread.demo5.TicketThread;public class Ticket implements Runnable{//对于多个线程来说,票数只有一份int num = 10;@Overridepublic void run() {while (true){if(num <= 0){break;}printTicket();}}public synchronized void printTicket(){if(num > 0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"买到了:"+num--);}}
}
package com.ffyc.javathread.demo6;public class Test {public static void main(String[] args) {//创建了一个出票的任务Ticket ticket = new Ticket();Thread t1 = new Thread(ticket,"窗口1");Thread t2 = new Thread(ticket,"窗口2");t1.start();t2.start();}
}
运行
加锁方式2:
使用jdk中提供的ReentrantLock类实现加锁
ReentrantLock只能对某一段代码块加锁,不能对整个方法加锁
代码
package com.ffyc.javathread.demo7;import java.util.concurrent.locks.ReentrantLock;public class TicketTask implements Runnable{int num = 10;//提供一个实现加锁的对象ReentrantLock reentrantLock = new ReentrantLock();@Overridepublic void run() {while (true){//加锁reentrantLock.lock();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {if(num > 0){System.out.println(Thread.currentThread().getName()+":买到了"+num--);}else {break;}}finally {//在finally块中保证锁必须释放//释放锁reentrantLock.unlock();}}}
}
package com.ffyc.javathread.demo7;public class Test {public static void main(String[] args) {TicketTask ticketTask = new TicketTask();Thread t1 = new Thread(ticketTask,"窗口1");Thread t2 = new Thread(ticketTask,"窗口2");t1.start();t2.start();}
}
运行
synchronized 与 ReentrantLock区别
相同点:
都实现了加锁的功能
不同点:
1、synchronized是一个关键字,ReentrantLock是一个类
2、synchronized修饰代码块和方法ReentrantLock只能修饰代码块
3、synchronized可以隐式的加锁和释放锁,运行过程中如果出现了异常可以自动释放
ReentrantLock需要手动的添加锁和释放锁,建议在finally代码块中释放锁。
线程通信
线程间的通信(在同步代码块的基础上,使用wait,notify来对线程进行控制)
wait(); notify(); notifyAll();
这三个方法都是Object类中定义的方法
这三个方法必须在同步代码块中使用
这三个方法必须通过为锁的对象调用
代码
package com.ffyc.javathread.demo8;public class PrintNumThread extends Thread{static int num = 0;static Object o = new Object(); @Overridepublic void run() {while (true){synchronized (o){//唤醒等待中的线程o.notify();if(num < 100){System.out.println(Thread.currentThread().getName()+":"+ ++num);}else {break;}try {//让线程等待,同时释放锁,等待的线程不能自己醒来,必须让另一个线程唤醒o.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}
}
package com.ffyc.javathread.demo8;public class Test {public static void main(String[] args) {PrintNumThread p1 = new PrintNumThread();p1.setName("线程1");PrintNumThread p2 = new PrintNumThread();p2.setName("线程2");p1.start();p2.start();}
}
运行
......
新增创建线程方式
实现Callable接口,重写call()方法
call()可以有返回值,可以抛出异常,还可以自定义返回结果的类型
代码
package com.ffyc.javathread.demo10;import java.util.concurrent.Callable;public class SumTask<T> implements Callable<T> {@Overridepublic T call() throws Exception {Integer i = 0;for (int j = 0; j < 10; j++) {i+=j;}return (T)i;}
}
package com.ffyc.javathread.demo10;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Test {public static void main(String[] args) {SumTask<Integer> sumTask = new SumTask<>();FutureTask<Integer> futureTask = new FutureTask(sumTask);Thread thread = new Thread(futureTask);thread.start();try {//获取到线程执行结果Integer integer = futureTask.get();System.out.println(integer);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}