线程
-
进程和线程
进程和线程的概念:
- 进程:就是一个运行的程序,默认有一个主线程
- 线程:是进程中的一个任务,在一个进程中可以同时执行多个任务,称为多线程
进程和线程的区别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aew9mkvX-1629291715338)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210818094255264.png)]
线程的组成:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QdXW0FeC-1629291715340)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210818094746071.png)]
线程的逻辑代码
-
Java的线程实现方式
-
继承Thread类
package com.qf.pro2103.day18; public class TestThread { public static void main(String[] args) { // TODO Auto-generated method stub Thread1 t1=new Thread1(); Thread2 t2=new Thread2(); t1.start();//可以让CPU执行任务,但不是马上执行 t2.start(); for(int k=1;k<=10;k++) { System.out.println("k="+k); } } }
package com.qf.pro2103.day18; public class Thread1 extends Thread { public void run() { for(int i=1;i<=10;i++) { System.out.println("i="+i); } } }
-
实现进程接口(Runnable)
package com.qf.pro2103.day18; public class Thread3 implements Runnable { @Override public void run() { // TODO Auto-generated method stub for(int i=1;i<=10;i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("i="+i); } } }
package com.qf.pro2103.day18; public class TestThread1 { public static void main(String[] args) { // TODO Auto-generated method stub Thread3 t3=new Thread3(); Thread4 t4=new Thread4(); Thread t31=new Thread(t3); Thread t41=new Thread(t4); t31.start(); t41.start(); } }
推荐使用接口
-
-
线程的状态及状态转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUnRA6QR-1629291715342)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210818111230229.png)]
程序运行状态的线程进入到阻塞状态的方式之一:sleep(毫秒)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I5SxDt1w-1629291715344)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210818111457716.png)]
-
后台线程,线程让步,线程优先级等
sleep(毫秒): 正在执行状态的线程,执行了sleep方法后就阻塞进入休眠状态,待休眠时间结束后,此线程自动进入就绪状态。
yield()线程放弃,正在执行的线程,如果执行了yield方法,此线程由执行状态转变为就绪状态,等待CPU再次轮转
join()线程结合,插队 当前线程正在执行过程中,如果某个线程执行了join(),则优先执行该线程,待该线程执行后,继续执行被插队的线程
setPriority(10); //级别的数值越大,被CPU先执行的几率越高
t2.setDaemon(true); 设置为后台线程
-
线程安全问题:
多个线程操作同一“资源”
package com.qf.pro2103.day18;
public class MyArrayList implements Runnable {
public static int index=0; //记录数组存放数据的位置
public static String[] ary=new String[5];
@Override
public void run() {
// TODO Auto-generated method stub
ary[index]="hello";
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
index++;
}
}
package com.qf.pro2103.day18;
public class ThreadSafe {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyArrayList mal=new MyArrayList();
Thread t1=new Thread(mal);
Thread t2=new Thread(mal);
t1.start();
t2.start();
//输出数组内容
try {
Thread.sleep(1000);//线程给数组复制后,再执行下面的遍历
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String[] ary=MyArrayList.ary;
System.out.println("index="+MyArrayList.index);
for(int i=0; i<ary.length; i++){
System.out.println(ary[i]);
}
}
}
解决线程安全问题:使用锁
用锁把相关代码锁在一起,即使CUP轮转到其它线程,但是由于有锁,其它线程只能等待(阻塞),当锁释放后,其它线程竞争锁(CPU时间片轮转到哪个线程),
同步锁有两种用法:
1、同步代码块
synchronized(锁对象){
//锁住的代码
}
package com.qf.pro2103.day18;
public class MyArrayList implements Runnable {
public static int index=0; //记录数组存放数据的位置
public static String[] ary=new String[5];
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (this) {
ary[index]="hello";
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
index++;
}
}
}
2、同步方法
用法:在方法的返回值类型前加关键字synchronized,意味着方法中的所有代码都在锁的范围内。
6.死锁问题
两个线程,互相等待对象释放锁对象
package com.qf.pro2103.day18.deadlock;
public class FoodLock {
//任意非空对象都可以作为锁对象
//一个锁对象只能同时为一个线程作为锁对象使用
public static Object milk=new Object();
public static Object bread=new Object();
}
package com.qf.pro2103.day18.deadlock;
public class Teacher implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (FoodLock.bread) {
System.out.println("Teacher已经拿到了面包,等待拿到牛奶");
synchronized (FoodLock.milk) {
System.out.println("Teacher已经拿到了牛奶,可以吃早餐了!");
}
}
}
}
package com.qf.pro2103.day18.deadlock;
public class Student implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (FoodLock.milk) {
System.out.println("Student已经拿到了牛奶,等待获取面包");
synchronized (FoodLock.bread) {
System.out.println("Student拿到了面包,可以吃早餐了!");
}
}
}
}
package com.qf.pro2103.day18.deadlock;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student jia=new Student();
Teacher liu=new Teacher();
Thread t1=new Thread(jia);
Thread t2=new Thread(liu);
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
避免死锁:
1、确定线程的先后执行顺序 join()
总结:
1、进程和线程区别
2、线程实现方式
3、线程的状态及状态的改变
4、重点:什么是线程安全问题
5、同步锁:两种用法
d catch block
e.printStackTrace();
}
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
避免死锁:
1、确定线程的先后执行顺序 join()
总结:
1、进程和线程区别
2、线程实现方式
3、线程的状态及状态的改变
4、重点:什么是线程安全问题
5、同步锁:两种用法
6、死锁问题