进程与线程。
计算机cpu运行有并发和并行两种。
在多核时并行运算时,一个进程可以有多个线程。
我们这样类比,计算机运行环境好似工业园区,资源,空间一定。这样就难免要对资源进行分配。在计算机运行时,采取先到先得的分配方式。所以,每一个进程(单元楼)有可以有多个线程(工作室)
工作室(即Thread)有用两种,Thread子类,Runnable实现类。
Thread体系:
Thread ->实现于 Runnable接口
所以 此两种方法都要重写 run方法。
我们知道线程对象需要start()调用后才能执行run方法。
start()方法底层有 用C++编写的start0()方法,以使用系统底层函数调用系统资源。
线程的基本使用
这里用售票系统展示(包含线程安全的使用)
public class Mythread extends Thread {
private static int i=100; //静态变量+线程安全 —> 多对象锁信息安全
public synchronized void run(){
// synchronized (this) { //同步代码块,锁住当前对象,避免线程冲突。
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// while (i > 0) {
// System.out.println(Thread.currentThread().getName() + " 剩余:" + (--i));
// }
//
// }
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} while (i > 0) {
System.out.println(Thread.currentThread().getName() + " 剩余:" + (--i));
}
}
public static void main(String[] args) {
Mythread t1 = new Mythread();
t1.setName("Teacher");
Mythread t2 = new Mythread();
t1.start();
t2.start();
Mythread2 t3 = new Mythread2();
Mythread2 t4 = new Mythread2();
// synchronized (t3){ //Runnable实现类,没有Start方法,故采用线程类 进行静态代理
// new Thread(t3).start(); //Runnable实现类,锁住当前对象,避免线程冲突。
// new Thread(t3).start();
// new Thread(t3).start();
// }
}
}
class Mythread2 implements Runnable {
private static int i=100; //静态变量+线程安全 —> 多对象锁信息安全
public synchronized void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
while (i > 0) {
System.out.println(Thread.currentThread().getName() + " 剩余:" + (--i));
}
}
}
通知方式
程序要求线程在特定情况中断。那么就可以使用 通知方式 终止线程。
通知方式:在线程中设置 控制变量 。同时提供set方法
线程方法
https://www.runoob.com/manual/jdk11api/java.base/java/lang/Thread.State.html#
interrupt,中断线程,不是终止。所以一般用于中断正在休眠的线程。
线程插队
yield在资源充沛时不一定成功,涉及内核态。
join一定成功,注意用插入者调用join();
但两者(插入者和被插入者)应当是主线程和子线程的关系。
public class Mythread extends Thread {
private static int i=100;//静态变量+线程安全 —> 多对象锁信息安全
public Mythread() {}
public Mythread(String name) {
super(name);
}
public static int getI() {
return i;
}
public synchronized void run(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} while (i > 0) {
System.out.println(Thread.currentThread().getName() + " 剩余:" + (--i));
}
}
public static void main(String[] args) throws InterruptedException {
Mythread t1 = new Mythread();
t1.setName("Teacher");
Mythread t2 = new Mythread("小弟");
t2.start();
for (int j = 0; j < 20; j++) {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"呼吸了"+j+"次!");
if(j==10)t2.join(); //让子线程插队
}
}
用户线程和守护线程
如果我们希望当main线程结束时,子线程自动结束
只需将子线程设置为守护线程。 调用 setDaemon()
public static void main(String[] args) throws InterruptedException {
Mythread t1 = new Mythread();
t1.setName("Teacher");
Mythread t2 = new Mythread("小弟");
t2.setDaemon(true); //设置为守护线程,后执行。其能在主线程终止后自动终止
t2.start();
for (int j = 0; j < 20; j++) {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"呼吸了"+j+"次!");
//if(j==10)t2.join();
}
}
线程七大状态
线程内部枚举 State
New 线程未启动的线程状态
线程同步机制
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就需要使用线程同步访问技术,保证数据在任何同一时刻,最多只用一个线程访问,以保证数据的完整性。
2.也可以这样理解:线程同步,即当用一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成,其他线程才能该内存地址进行操作。
同步具体方法--synchronized关键字
1.synchronized代码块(推荐)整体效率较高
2.synchronized方法声明 成员方法和静态方法呈现不一样
如果是成员方法或变量加锁,那么就用Runnable实现类对象,对同一对象进行多次静态代理多线程同步
如果是Thread子类实现多线程同步,则需将所需同步的代码设计为静态代码(静态方法或变量)因为Thread子类对象不能创建多个线程。
同步原理(对象锁)
对象某一位表示对象锁(互斥锁)标记
public static int getI() {
//对于静态方法,锁加在类本身上。
synchronized (Mythread.class) {
return i;
}
}
public void m1() {
//对于普通方法,锁加在当前对象上。
synchronized (this){
//...........
}
}
线程死锁
线程死锁是指两个或多个线程由于互相持有对方所需要的资源而无法继续执行的情况。当多个线程同时占用资源,并等待其他线程释放它们所需要的资源时,可能会导致死锁。在死锁状态下,各线程都处于Blocked状态,无法继续执行,从而导致程序无法正常终止。
产生线程死锁的四个必要条件是:
1.互斥条件:资源在某一时刻只能被一个线程占用。
2.请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:线程已获得的资源在使用完毕前,不会被其他线程剥夺。
4.循环等待条件:两个或两个以上的线程形成循环等待资源的关系。