多线程
1.相关概念
并发:指两个或多个事件在同一段时间里发生
并行:指两个或多个事件在同一时刻发生(同一时间)
程序:有某种编程语言编写的一套有序的指令
软件:由若干个程序+其他资源组成的软件(qq由后台程序+前台静态资源)
进程:软件的运行时系统为其开辟的一块独立的空间
线程:是进程的最小单位,一个进程必定最少有一个线程,如果有多线程则被称为多线程程序
2.线程的调度机制
分时调度--->平均
抢占式调度----->优先级高的抢的多,如果优先级一样,cpu随机选择,java采用这个
3.线程的创建和启动
A.继承Thread类
①新建一个类
②继承Thread类
③重写父类run方法(当前运行的主题)
④实例化一个当前类的对象(线程对象)
⑤启动线程(线程对象.start())
B.实现Runnable接口
①新建一个类
②实现接口Runnable接口
③实现父接口run方法(当前运行的主题)
④实例化一个当前类的对象(目标对象)
⑤实例化一个Thread类的对象,将目标设置进去(线程对象)
//Thread red =new Thread(目标对象的对象名);
⑥启动线程
4.Thread线程核心类
必须调用的Thread类的构造方法
构造方法:
new THread() 供子类使用
new Thread(String name) 供子类使用 (---》String name 线程的名)
new Thread(Runnable target) 自己实例化,并设置目标
new Thread(Runnable target,String name) 自己实例化,并设置目标 (---》String name 线程的名)
方法:
public String getName() 获取当前线程的名称
public void run() 此线程要执行的任务在此处定义代码
public static Thread currentThread() 返回当前线程正对执行线程对象的引用
[Thread-0,5,main] 【线程名,线程优先级,调用的方法】
public final boolean isAlive():测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。
优先级:【1-10】
public final int getPriority() :返回线程优先级
public final void setPriority(int newPriority) :改变线程的优先级
线程的默认优先级和创建他的那个线程优先级一致
main方法的线程优先级默认是5!
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static void yield():线程的礼让
void join() :线程的插队
void join(long millis) :
void join(long millis, int nanos) :
练习:
创建一个子线程,打印0-100之间的偶数,创建一个子线程,打印0-100之间的奇数
main方法就作为启动这两个子线程的线程
/**
* 练习:
* 创建一个子线程,打印0-100之间的偶数,创建一个子线程,打印0-100之间的奇数
* main方法就作为启动这两个子线程的线程
*/
public class Test1 {
public static void main(String[] args) {
Treand1 t1 = new Treand1();
Treand2 t2 = new Treand2();
t1.start();
t2.start();
}
}
public class Treand1 extends Thread {
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if (i%2==0)
System.out.println(this.getName()+"这是偶数="+i);
if (i==50) {
try {
this.join(1000);//如果不设置时间 此线程会一致在50这里卡住
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Treand2 extends Thread {
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if (i%2==1)
System.out.println(this.getName()+"这是奇数="+i);
if (i==9){
this.yield();
}
}
}
}
线程安全
1.同步代码块
语法:synchronized(锁对象){需要上锁的代码}
需要上锁的代码:在保证程序正确的前提下,代码越少越好
锁对象:任意Java对象,但是要报错多个线程想要互斥,那么这多个线程的锁对象必须是同一个那么者多个线程
1.2 同步方法
语法:在方法上添加修饰符synchronized
普通方法的锁对象默认是 this
静态方法的锁对象默认是 this.getClass()
任意的String对象 如“java” “a” “ads"
练习:买票问题?
接口:
public class Tickets2 implements Runnable {
private int num= 100;
@Override
public void run() {
{
//为了防止重复,限制每次只能进入一个线程 增加同步锁(互斥锁)
while (true){
System.out.println("请问您买票吗");
//一般用: this this.getClass() 常量字符串
synchronized (this) { //String对象 / this.getClass
if (num>=0){
System.out.println(Thread.currentThread().getName()+"买到一张票,剩余票数="+num);
--num;
}else {
break;
}
}
}
}
}
}
public class Test2 {
public static void main(String[] args) {
Tickets2 tickets = new Tickets2();
Thread t1 = new Thread(tickets);
Thread t2 = new Thread(tickets);
Thread t3 = new Thread(tickets);
t1.start(); //这是调用线程
t2.start();
t3.start();
}
}
继承:
public class Tickets extends Thread{
private static int num =100; //总票数
@Override
public void run() {
//为了防止重复,限制每次只能进入一个线程 增加同步锁(互斥锁)
while (true){
System.out.println("请问您买票吗");
//一般用: this this.getClass() 常量字符串
synchronized ("s") { //因为同步代码块的要求 所以这里只能采用this.getClass() 常量字符串
if (num>=0){
System.out.println(Thread.currentThread().getName()+"买到一张票,剩余票数="+num);
--num;
}else {
break;
}
}
}
}
}
/**线程
* 继承
* 同步代码块
* 要求:多个线程的锁对象必须是同一个
*/
public class Test1 {
/**
* 单元测试不能测试线程
* @param args
*/
public static void main(String[] args) {
Tickets tickets1 = new Tickets();
Tickets tickets2 = new Tickets();
Tickets tickets3 = new Tickets();
tickets1.start(); //这是调用线程
tickets2.start();
tickets3.start();
// tickets1.run(); //这是调用方法
// tickets2.run();
// tickets3.run();
}
}
**线程的通信**
等待唤醒---.> 生产者和消费者模式
等待:
wait()
wait(long timeout)
wait(long timeout,int nanos)
运行等待方法的线程进行等待
调用等待方法的对象是监视器对象(就是后期唤醒该线程的依据)
要求:等待方法必须运行在**锁内**,并且锁对象必须和监视器对象**同一个**
原理:当前线程执行等待方法后,会释放锁资源
在被唤醒之后,依然会判断锁资源是否存在,如果不存在就等着
如果存在的话,是接着上次等待的位置往下执行!
唤醒:
notify()
唤醒的是对象监视器下等待的一个线程
notifyAll()
唤醒的是对象监视器下等待的所有线程
要求:唤醒方法必须运行在**锁内**,并且**锁对象必须和监视器对象同一个**
练习: 一对一吃包子的问题
public class Test2 {
public static void main(String[] args) {
//创建一个包子对象
BaoZi baoZi=new BaoZi();
BaoZiPu baoZiPu=new BaoZiPu(baoZi);
ChiHuo chiHuo=new ChiHuo(baoZi);
baoZiPu.start();
chiHuo.start();
}
}
/**
* 练习:一对一吃包子 一个生产者和一个消费者
*/
public class ChiHuo extends Thread {
private BaoZi baoZi;
public ChiHuo(BaoZi baoZi){
this.baoZi=baoZi;
}
@Override
public void run() {
while(true){
//包子不存在,自己等待
synchronized ("java") {
if(!baoZi.isFlag()){
try {
"java".wait(); //等待的时候会释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("开始吃包子");
baoZi.setFlag(false);
System.out.println("包子吃完了,请继续制作");
"java".notify();
}
}
}
}
public class BaoZiPu extends Thread {
private BaoZi baoZi;
public BaoZiPu(BaoZi baoZi){
this.baoZi=baoZi;
}
@Override
public void run() {
while(true){
//包子存在,自己等待
synchronized ("java") {
if(baoZi.isFlag()){
try {
"java".wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("开始制作包子");
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第"+(i+1)+"步");
}
baoZi.setPer("白面");
baoZi.setXian("猪肉大葱");
baoZi.setFlag(true);
System.out.println("包子制作完成,吃货来吃");
"java".notify();
}
}
}
}
public class BaoZi {
private String per;
private String xian;
private boolean flag =true;//记录包子资源是否存在 true就是存在 false就是不存在
public String getPer() {
return per;
}
public void setPer(String per) {
this.per = per;
}
public String getXian() {
return xian;
}
public void setXian(String xian) {
this.xian = xian;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
**线程的生命周期**
①新生线程-------->线程对象刚刚被实例化出来
②就绪线程---------->调用start方法
③运行线程---------->线程得到执行权(先进入同步锁,关闭同步锁)
④阻塞线程
睡眠 slee()
插队 join()
等待 wait()
锁 synchronized
IO阻塞
........
⑤死亡线程
run方法结束-->正常结束/非正常结束(system.exit(0))
**实现接口Runnable的好处**
a. 避免了java中的单继承的局限性
b. 可以更好的处理共享资源
c. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
d. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
**释放锁和不释放锁**
释放锁:
a. 上锁的代码运行完了(锁方法/锁代码块运行完;break,return终止了锁方法/锁代码块;代码出现异常报错,结束了运行)
b. 执行了wait方法
不释放锁:
a. 上锁的代码没有运行完
b.Thread.sleep()、Thread.yield()(暂停当前线程的执行)
死锁 -> 一定要避免的
sleep和wait的区别
sleep不会释放锁
wait会释放锁
泛型
概念:在定义一些内容的时候,有一些数据的数据类型不能够确定,就定义为泛型
然后在使用的时候在确定其类型
好处:可以避免强转,减少代码量和异常出现的几率
Comparable
Comparator
/**
* 泛型的应用
*/
public class Test1 {
public static void main(String[] args) {
//范围没有基本类型,基本类型用包装类代替
Student<Double> s1 = new Student<Double>();
s1.setScore(55.7);
// 泛型可以只前面定义 后面可以省略
Student<String> s2 = new Student();
s2.setScore("优秀");
System.out.println(s1);
System.out.println(s2);
}
}
public class Student<T> {
private String name;
/**
*eg:数学:double 98.5 99.5 875
* 语文:string 优良差
* 英语:A B C
* 同一个变量,在不同的环境下有不同的类型表示,这时候可以定义为泛型
*/
private T score;
public Student() {
}
public Student(String name, T score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
import java.util.Comparator;
public class Student implements Comparator<Student> {
private String name;
/**
*eg:Comparable
*/
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compare(Student o1, Student o2) {
return o1.score-o2.score;
}
}
泛型的定义:一般是一个大写字母
1.类、接口
语法:类名/接口名<字母1,字母2....>
要求:泛型可以作为属性,形参,返回值,但是不可以使用在静态资源上面 (泛型是在实例化的时候才知道类型,才去开辟空间,而静态类却是在类加载前都开辟了空间)
使用:
实例化对象:
Student<String,Integer> student=new Student<>();
泛型的类型必须是引用数据类型
泛型要么都指定类型,要么都不指定类型(类型默认是Object)
作为父级:
a. 在继承的时候就确定父类的泛型类型
public class Sub extends Student<String,Double> {}
每次实例化子类对象的时候,泛型都是固定的啊
b. 在实例化子类对象的时候确定父类的泛型类型
public class Sub<T,A> extends Student<T,A> {}
Sub<String,Double> sub=new Sub<>();
每次实例化子类对象,都可以设置不同的泛型类型
2.方法
3. 类型变量的上限(父类或父接口的类型)
<类型 extends 上限>
上限可以是类,也可以是接口
要求:如果上限是类的话,只能有一个
如果上限是接口,可以有多个
如果类和接口同时存在,可以,但是需要满足上述要求,另外类必须在首位
4. 泛型的擦除
如果不指定泛型的实际类型,则采用泛型的上限类型
5. 泛型方法
将泛型定义在方法上,泛型的使用范围只仅限于当前方法
语法:在方法修饰符的后面添加泛型代码
public static<T extends Comparable> void sort(T[] arrs){
注意:当前泛型只能在当前方法内使用
方法上的泛型在确定类型是通过,实参的类型确定的
import java.util.Arrays;
import java.util.Comparator;
public class Utils<T> {
//静态类和静态方法并不属于静态资源
public static<T extends Comparable> void method(T[] arrs){ //这里可以使用static
//冒泡
for (int i = 0; i < arrs.length-1; i++) {
for (int j = 0; j < arrs.length-i-1; j++) {
if (arrs[j].compareTo(arrs[j+1])<0){
T temp =arrs[j];
arrs[j]=arrs[j+1];
arrs[j+1]=temp;
}
}
}
}
public static<T extends Comparator> void method1(T[] arrs){ //这里可以使用static
Arrays.sort(arrs);
}
}
/**
* 泛型的应用
*/
public class Test1 {
public static void main(String[] args) {
Student[] ly = new Student[7];
ly[0] = new Student("ly", 5);
ly[1] = new Student("ly", 52);
ly[2] = new Student("ly", 59);
ly[3] = new Student("ly", 5);
ly[4] = new Student("ly", 51);
ly[5] = new Student("ly", 25);
ly[6] = new Student("ly", 65);
Utils.method1(ly);
for (Student student : ly) {
System.out.println(student);
}
System.out.println();
Utils<Student> s = new Utils<>();
s.method(ly);
for (Student student : ly) {
System.out.println(student);
}
}
}
import java.util.Comparator;
public class Student implements Comparable<Student>,Comparator<Student>{
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compare(Student o1, Student o2) {
return o1.score-o2.score;
}
@Override
public int compareTo(Student o) {
return this.score-o.score;
}
}
6 泛型的通配符
一般用在方法的形参
<?> 任意类型 默认就是任意类型
<? extends 上限类型> 对局部的要求上限类型(上限类型或者上限类型的子类)
<? super 下限类型> 对局部的要求下限类型(只能是下线类型或者是下线类型的父类)