内容大纲
线程相关概念
并发并行
当只有一个CPU时,会执行并发的效果,在多个应用程序之间快速切换,而有多个CPU时,则多个CPU独立执行,而当进程多于CPU个数时,则会出现并发+并行的情况,总有一个CPU需要来回切换从而并发。
查看电脑CPU个数的API
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
int i = runtime.availableProcessors();
System.out.println("当前电脑的CPU个数为:"+i);
}
线程的基本使用
创建线程
创建线程的两种方式
线程创建案例
class Cat extends Thread{//继承Thread类,来重写run()方法,该run()方法也是Thread类对Runnable接口的实现
@Override
public void run() {
while(true){//无限循环广播
//super.run();重写run方法,写上自己的业务逻辑
//要求:每隔1秒中输出:迷你深渊冰龙
System.out.println("<>迷你深渊冰龙<>");
//让线程休眠1秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
run()方法会被对象的start()方法调用,在run()方法中调用的sleep(),表示线程休眠,参数是毫秒值,1000=1秒
多线程机制
即使是main线程结束,但是如果main的子线程还没有结束,那么这个进程就不会结束。进程=Application
Start()方法
为什么在main方法中调用的是start(),而不是run()方法?
如果此处是cat.run(),那么当主线程启动时,执行将又变成顺序执行,此时run()方法里输出的线程名就不再是Thread-0,而是main,也就是说,start()方法才是真正开启了一个线程,run()方法只是声明线程要执行的内容,下面是start方法的底层。
Runnable创建线程
多线程案例
继承Thread和实现Runnable的区别
多线程售票问题
不论是使用继承Thread还是实现Runnable接口都会出现超卖的情况,原因是三个线程都进入了判断,轮流执行减票的情况。
通知线程退出
下面代码有一个线程类t1,该类会通过while循环一直打印语句到控制台,while的判断条件是loop变量,我们设置了setLoop()方法,在主线程中,让主线程休眠10秒后,进行setLoop(false),也就是通知t1线程停止。
package com.hspdu.thread;
public class ThreadExit {
public static void main(String[] args)throws Exception {
T t1 = new T();
t1.start();
//如果希望main线程去控制t1线程的终止,必须可以修改loop变量
//让t1退出true循环,从而终止t1线程 -> 通知方式
//让主线程休眠十秒,再通知t1退出
Thread.sleep(10*1000);
t1.setLoop(false);
}
}
class T extends Thread{
private int count = 0;
private boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while(loop){
try{
Thread.sleep(50);//让当前线程休眠10ms
}catch (Exception ex){
ex.printStackTrace();
}
System.out.println("T 运行中..."+(++count));
}
}
}
线程中断和线程常用方法
package com.hspdu.thread;
public class ThreadMethodTest {
public static void main(String[] args)throws InterruptedException {
MyThread t = new MyThread();
t.setName("沙鹰-天神");//自定义线程名
t.setPriority(Thread.MIN_PRIORITY);//最小优先级 = 1
t.start();//启动子线程
//主线程打印5个hi,然后就中断 子线程休眠
for (int i = 0; i <5 ; i++) {
Thread.sleep(1000);
System.out.println("hi"+i);
}
System.out.println(t.getName()+" 线程的优先级 = "+t.getPriority());
//当执行到这里时就会中断t线程的休眠
t.interrupt();//中断t线程
}
}
class MyThread extends Thread{
@Override
public void run() {
while(true){
for (int i = 0; i < 100 ; i++) {
//Thread.currentThread().getName() 获取当前线程名称
System.out.println(Thread.currentThread().getName()+" 吃包子~~~"+i);
}
try{
System.out.println(Thread.currentThread().getName()+" 休眠中~~~");
Thread.sleep(5000);
}catch (InterruptedException ex){
//当线程执行到一个interrupt方法,就会catch一个异常,可以加入自己的业务代码
//InterruptedException 捕获到一个中断异常
System.out.println(Thread.currentThread().getName()+" 被interrupt了");
}
}
}
}
线程插队
package com.hspdu.thread;
public class ThreadYield {
public static void main(String[] args)throws Exception {
ThreadT t = new ThreadT();
t.start();
int count = 0;
boolean flag = true;
while(flag){
if (count==5){
t.join();//加入
Thread.yield();//礼让,不一定成功
}
if(count==20){
break;
}
System.out.println(Thread.currentThread().getName()+"输出了 "+(++count)+"次");
}
}
}
class ThreadT extends Thread{
@Override
public void run() {
int count = 0;
boolean flag = true;
while(flag){
if(count==20){
break;
}
System.out.println("沙鹰-天神"+Thread.currentThread().getName()+"输出了 "+(++count)+"次");
}
}
}
守护线程
package com.hspdu.thread;
public class ThreadShouHu {
public static void main(String[] args) throws Exception {
MyShouHuThread shou = new MyShouHuThread();
//如果我们希望主线程结束时子线程也结束
//只需将子线程设为守护线程即可
shou.setDaemon(true);//设置为守护线程
shou.start();
for (int i=0;i<=10;i++){
System.out.println("主线程执行中");
Thread.sleep(1000);
}
}
}
class MyShouHuThread extends Thread{
@Override
public void run() {
for (;;){//无限循环
try{
Thread.sleep(1000);
}catch (Exception ex){
ex.printStackTrace();
}
System.out.println("MyShouHuThread线程执行中");
}
}
}
线程七大状态
线程同步
线程同步通过关键字:synchronized实现
在方法上添加该关键字,此时方法变为同步方法,同步方法的概念是锁,线程必须单独执行。
在先前售票程序中,窗口会出现超额出售票的情况,此时将run()方法的代码写到一个同步方法中,再调用,就可以避免出现超额出售票的情况。
互斥锁
同步方法的原理是给对象添加一把锁,线程们会去抢这把锁,具体是哪个线程拿到锁是不确定的,非公平锁。
线程死锁
释放锁
线程练习
598