目录
引言
2021年3月2日21:55:46
知识均总结与: Java全栈学习网站. 以及自己的一些心得体会
基本概念
-
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。
-
实现多线程是采用一种并发执行机制 :
并发执行机制原理:简单地说就是把一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,从而达到多个应用程序在同时进行的效果 。
多线程: 是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。这就是多线程程序 。 -
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态概念。
-
进程则是执行程序的一次过程,他是一个动态概念,是系统资源分配的单位
-
通常一个进程包含若干线程,线程是CPU调度和执行的单位。
-
main()线程称为主线程,是系统的入口,用于执行整个程序。
-
线程的调度由调度器安排调度,调度器是与操作系统密切相关的,无法人为干预。
创建线程
三种方法:继承Thread类、实现Runnable接口、实现callable接口
继承Thread类
第一步:继承Thread类
第二步:重写run方法
第三步:创建对象,调用start开启线程
package work;
//创建线程方法一:继承Thread,重写run方法,调用start开启线程
//线程开启不一定立即执行,由cpu调度执行
public class Test extends Thread{
public void run() {
//run()方法线程的载体。不用显示调用,开启线程后会自动执行。
for(int i = 0; i < 100; i++) {
System.out.println("学习多线程");
}
}
public static void main(String[] args) {
Test thread1 = new Test();
thread1.start();
for(int i = 0; i < 1000; i++ ) {
System.out.println("我是主线程");
}
}
}
注:不建议使用,避免单继承局限性
实现Runnable接口
第一步:定义一个类实现Runnable接口
第二步:实现run()方法
第三步:创建线程对象,调用start开启线程
package work;
//创建线程方法二
//线程开启不一定立即执行,由cpu调度执行
public class Test implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
for(int i = 0; i < 100; i++) {
System.out.println("努力学习");
}
}
public static void main(String[] args) {
Test t1 = new Test();
new Thread(t1).start();
for(int i = 0; i < 1000; i++ ) {
System.out.println("我是主线程");
}
}
}
注:推荐使用,避免了单继承的局限性,方便同一对象被多个线程使用
练习:模拟龟兔赛跑
package work;
//练习,模拟龟兔赛跑
public class TestThread implements Runnable {
private static String winner;
@Override
public void run() {
// TODO 自动生成的方法存根
for(int i = 0; i <= 1000; i++) {
if(Thread.currentThread().getName().equals("兔子") && i%10==0) { //模拟兔子睡觉
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
if(gameOver(i)) {
break;
}
System.out.println(Thread.currentThread().getName() + "---->跑了" + i + "步" );
}
}
//判断胜负
private boolean gameOver(int steps) {
if(winner != null) { //出现胜利者
return true;
}
if (steps >= 1000) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}
public static void main(String[] args) {
TestThread t1 = new TestThread();
new Thread(t1,"兔子").start();
new Thread(t1,"乌龟").start();
}
}
实现Callable接口(了解)
步骤:
- 实现Callable 接口,需要返回值类型
public class TestThread implements Callable<Boolean> {
- 重写call方法,需要抛出异常,注意返回值类型要与接口那里一致
public Boolean call() {
-
创建目标对象
-
创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);//1表示创建一个线程
- 提交执行
Future<Boolean> r1 = ser.submit(t1);
- 获取结果
boolean rs1 = r1.get();
- 关闭服务
ser.shutdownNow();
package work;
import java.util.concurrent.*;
//线程创建方式三,创建Callable接口
public class TestThread implements Callable<Boolean> {
private static Object ExecutorService;
public Boolean call() {
for(int i = 0; i < 1000; i++) {
System.out.println("我是Callable接口实现的线程");
}
return true;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
TestThread t1 = new TestThread();
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
//获取结果
boolean rs1 = r1.get();
//关闭服务
ser.shutdownNow();
for(int i = 0; i < 1000 ; i++) {
System.out.println("我是主线程");
}
}
}
静态代理模式
这是一种思想,真实对象专注于自己的事情,代理对象去处理真实对象做不了的事情
前面的Runnable接创建线程,就是利用了这种思想。
package work;
//总结
/*
* 真实对象和代理对象都要实现同一个接口
* 我们的在前面使用Runnable接口创建线程时,就使用了这种思想
*
* */
public class Test{
public static void main(String[] args) {
You you = new You();
new Company(you).happyMarry();
}
}
interface Marry{
void happyMarry();
}
class You implements Marry{
public void happyMarry() {
System.out.println("开开心心结婚了");
}
}
class Company implements Marry{
private Marry target;
public Company(Marry target) {
this.target = target;
}
public void happyMarry() {
before();
this.target.happyMarry();
after();
}
public void before() {
System.out.println("布置现场");
}
public void after() {
System.out.println("收钱");
}
}
Lamda表达式(类的一种书写方式)
好处:
避免匿名内部类定义过多
代码更简洁,只留下核心的逻辑
函数式接口:接口中只包含一个抽象方法,那么它就是一个函数式接口,对于函数式接口,我们可以使用Lambda表达式创建该接口的对象。
interface Marry{
void happyMarry();
}
我们的Runnable接口同样可以用Lambda表达式创建对象。
例子:
package work;
/*
*推导Lambda表达式
*/
public class Test{
//3.静态内部类
static class Love1 implements ILove{
public void lambda(int a) {
System.out.println("Lvoe----"+a);
}
}
public static void main(String[] args) {
ILove love = new Love();
love.lambda(0);
love = new Love1();
love.lambda(1);
//4.局部内部类
class Love2 implements ILove{
public void lambda(int a) {
System.out.println("Lvoe----"+a);
}
}
love = new Love2();
love.lambda(2);
//5.匿名内部类,没有类的名称,必须借助接口或者父类
love = new ILove(){
public void lambda(int a) {
System.out.println("Lvoe----"+a);
}
};
love.lambda(3);
//6.用Lambda表达式简化
love = (int a)->{
System.out.println("Lvoe----"+a);
};
love.lambda(4);
//Lambda表达式的简写 省略参数,如果有多个参数,要么都省,要么都不省
love =love = (a)->{
System.out.println("Lvoe----"+a);
};
love.lambda(5);
//省略括号,只有一个参数可以省略括号
love =love = a->{
System.out.println("Lvoe----"+a);
};
love.lambda(6);
//如果只有一条语句,可以省略花括号
love =love = a->System.out.println("Lvoe----"+a);
love.lambda(7);
}
}
//1.定义一个接口
interface ILove{
void lambda(int a);
}
//2.实现类
class Love implements ILove{
public void lambda(int a) {
System.out.println("Lvoe----"+a);
}
}
线程状态
五状态图
注:
- 线程对象一旦创建就进入新生状态
- 调用satart()方法后进入就绪状态
线程方法
停止线程
尽量使用自己的方法去终止线程
package work;
/*
*1.建议线程正常停止,-----》利用次数,不建议死循环
*2.建议使用标识位--------》设置一个标志位
*3.不建议使用stop或destroy等过时的或者JDK不建议使用的方法
*/
public class Test implements Runnable{
//1.设置一个标识位
private boolean flag = true;
public void run() {
int i = 0;
while(flag) {
System.out.println("run----"+i++);
}
}
//2.设置一个公共的方法停止线程,转换标志位
public void stop() {
this.flag = false;
}
//主线程
public static void main(String[] arg) {
Test t1 = new Test();
new Thread(t1).start();
for(int i = 0; i < 1000; i++) {
System.out.println("main thread"+i);
if(i == 900) {
//调用stop方法切换标志位,让线程终止
t1.stop();
System.out.print("==========线程终止了===========");
}
}
}
}
线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间到达后线程进入就绪状态
- 可以模拟网络延时,倒计时等
- 每个对象都有一个锁,sleep不会释放锁
模拟倒计时
package work;
import java.text.SimpleDateFormat;
import java.util.Date;
//倒计时
public class Test_1 {
public static void main(String[] args) {
//打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
while(true) {
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public static void tenDown() {
int num = 10;
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(num--);
if(num<=0) {
break;
}
}
}
}
线程礼让(yield)
- 让当前正在执行的线程暂停,但不阻塞
- 将线程从运行态转为就绪态
- 让cpu重新调度,礼让不一定成功
package work;
public class Test_2 {
public static void main(String[] args) {
MyYield myyield = new MyYield();
new Thread(myyield,"a").start();
new Thread(myyield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程强制执行(join)
- 待此线程执行完毕后,在执行其他线程,其他线程阻塞
- 可以想象成插队
package work;
public class Test_3 implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
for(int i = 0; i<=200; i++) {
System.out.println("VIP来了----" + i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
Test_3 t1 = new Test_3();
Thread thread = new Thread(t1);
thread.start();
//主线程
for(int i = 0; i< 500; i++) {
if(i == 200) {
thread.join(); //插队
}
System.out.println("main------"+i);
}
}
}
观测线程状态
- NEW:没有启动的线程处于此状态
- RUNNABLE:Java虚拟机中执行的线程处于此状态
- BLOCKED:阻塞的线程处于此状态
- WAITING:等待另一线程执行特定动作的线程处于此状态
- TIMED_WAITING:等待另一线程执行动作达到指定等待时间的线程
- TERMINATED:已退出的线程
- 通过thread.getState(); 获取线程当前状态
退出的线程无法再次被启动
package work;
//观察测试线程状态
public class Test_4 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()-> {
for(int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
//TIMED_WAITING 阻塞
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
System.out.println("");
});
//观察状态
Thread.State state = thread.getState(); //获取线程当前状态
System.out.println(state); //NEW
//观察启动后
thread.start();
state = thread.getState();
System.out.println(state); //RUNNABLE
while(state != Thread.State.TERMINATED) {//只要线程不终止,就一直输出状态
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调用哪个线程来执行
线程的优先级用数字表示(1——10)
- Thread.MAX_PRIORITY=10
- Thread.MIN_PRIORITY=10
- Thread.NORM_PRIORITY=10
改变优先级的方式与获取优先级
getPriority()、setPriority()
优先级高的不一定先执行
package work;
//测试线程优先级
public class Test_5 {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority());
MyPriority t = new MyPriority();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
Thread t6 = new Thread(t);
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY); //MAX_PRIORITY=10
t4.start();
t5.setPriority(Thread.MIN_PRIORITY); //MIN_PRIORITY=1
t5.start();
t6.setPriority(Thread.NORM_PRIORITY); //NORM_PRIORITY=5
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority());
}
}
守护线程
线程必须分为:用户线程、守护线程
- 虚拟机必须保护用户线程执行完毕
- 虚拟机不必等待守护线程执行完毕
- 守护线程:如,后台记录操作日志,垃圾回收等待等。
package work;
//测试守护线程
public class Test_6 {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true); //默认false表示用户线程,正常的线程都是用户线程
thread.start(); //上帝守护线程启动
new Thread(you).start();
}
}
class God implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
while(true) {
System.out.println("上帝守护你");
}
}
}
class You implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
for(int i = 0; i < 36500; i++) {
System.out.println("开开心心过完一生");
}
System.out.print("goodbye world!");
}
}