多线程
-
线程的创建和开启
-
线程的状态,新生、就绪、运行、阻塞、终止
-
线程安全
Thread类
1.进程:系统中的应用程序,具有自己的资源,内存空间,是系统运行的基本单位,一个进程包含1~n个线程。
2.线程:多个线程共享一个进程的资源和数据空间,每一个线程具有自己的程序计数器,线程是CPU调度的最小单位。
线程的创建
-
继承Thread ,重写run()方法,定义线程体;
-
实现Runnable接口,重写run()方法;
-
juc包下Callable接口,重写call()方法
实现Callable,重写call,在方法内部定义线程体(线程池)
a.call可以抛出异常
b.可以定义返回值
注:
- 类是单继承,接口是多实现
- 可以实现资源共享
练习:
1、模拟购票–>100张票,三个人买完
package com.yjx.thread;
public class Class03_web12306 implements Runnable{
static int ticket=100;
@Override
public void run() {
while (true){
if(ticket<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket--+"张票");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class03_web12306 web=new Class03_web12306();
Thread th1=new Thread(web,"张三");
Thread th2=new Thread(web,"李四");
Thread th3=new Thread(web,"王五");
th1.start();
th2.start();
th3.start();
}
}
2.网络图片下载
package com.yjx.thread;
import java.io.File;
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.net.URL;
public class Practice1 {
public static void main(String[] args) {
Thread th1=new Thread(new DownHtml("https://p4.img.cctvpic.com/photoworkspace/2021/07/22/2021072211110642797.jpg","src\\one.jpg"));
Thread th2=new Thread(new DownHtml("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","src\\two.png"));
Thread th3=new Thread(new DownHtml("https://p3.img.cctvpic.com/photoAlbum/page/performance/img/2021/7/22/1626946753175_318.jpg","src\\three.jpg"));
th1.start();
th2.start();
th3.start();
}
}
class DownHtml implements Runnable{
private String url;
private String path;
public DownHtml(String url, String path) {
this.url = url;
this.path = path;
}
@Override
public void run() {
Down.down(url,path);
}
}
class Down{
public static void down(String url,String path){
try {
FileUtils.copyURLToFile(new URL(url),new File(path));
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.模拟龟兔赛跑
package com.yjx.thread;
public class Practice2 implements Runnable{
private static String winner=null;
@Override
public void run() {
for (int step=1;step<=100;step++){
System.out.println(Thread.currentThread().getName()+"正在跑第"+step+"步");
if(step%10==0 && "兔子".equals(Thread.currentThread().getName())){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (checkwinner(step)){
break;
}
}
}
private boolean checkwinner(int step) {
if(winner!=null){
return true;
}
if(step==100){
winner=Thread.currentThread().getName();
System.out.println(Thread.currentThread().getName()+"赢了");
return true;
}
return false;
}
public static void main(String[] args) {
Practice2 bisai=new Practice2();
Thread th1=new Thread(bisai,"兔子");
Thread th2=new Thread(bisai,"乌龟");
th1.start();
th2.start();
}
}
线程状态
新生状态:new Thread,当前线程处于新生状态。
就绪状态:start(),这个线程进入就绪状态,线程准备好可以被CPU调度,会进 入就绪队列,等待CPU调度
运行状态:当CPU调度到这个线程,线程进入到运行状态开始执行。
阻塞状态:当程序无法正常执行,(进入阻塞状态有多种方式)
终止状态:线程结束
-
一个线程一旦终止,无法恢复
-
阻塞状态,阻塞解除会直接恢复就绪状态
进入阻塞状态的几种方式:sleep() 、join()、wait()、IO
进入就绪状态的几种方式:start()、yield()、线程切换、阻塞解除
进入终止状态的几种方式:stop(已过时)、正常执行完毕、添加标识 判断
-
操作线程的常用方法:
sleep():1.模拟网络延迟
2.模拟方法问题的可能性
yield():礼让线程,当一个线程调用yield,让出CPU的资源,进入到就绪状态
join():插队线程,可指定插队的时间(先就绪,后插队)
interrupt():主线程添加一个中断标识。
isInterrupt():测试此线程是否已被中断,是否调用过interrupt()方法添加中断 标识
interrupted():测试此线程是否已被中断,是否调用过interrupt()方法添加中 断标识,同时会复位标识 。
(一个线程如果终止,中断标识 也会复位)
getState():获取当前线程的状态
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
Priority线程的优先级
线程的优先级越高,执行的可能性就越大
1~10优先级的数值范围,默认5
setPriority()设置优先级
getPriority()获取优先级
守护线程
通常用户创建的线程默认是用户线程,守护线程是用来守护用户线程,当程序中所有的用户线程全部执行完毕,守护线程会直接结束
setDaeson(true)设置守护线程
isDaeson()判断线程是否为守护线程
注:java中的垃圾回收机制就是一个典型的守护线程
线程安全问题
多线程同时操作同一份资源才有可能遇到线程不安全问题
同步锁:让关键重点代码排队执行,其他代码依旧同时执行,在提高效率的基础 上保证数据安全。
synchronized 关键字
关注点:
-
限制条件:对象锁【每个对象只有一把锁】
-
同步的代码范围
范围大 效率低
范围小 锁不住
-
使用方式:
同步方法
同步块
锁的对象:this,类(类的class对象),资源