线程和进程

一、进程

  1. 进程是指运行中的程序,比如我们使用QQ,就启动该进程分配内存空间.
  2. 进程是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自升的产生,存在和消亡的过程

二、线程

  1. 线程是由进程创建的,是进程的一个实体
  2. 一个进程可以拥有多个线程
    • 一个想线程还可以创建它的子线程

三、其他概念

  1. 单线程:同时允许执行一个线程

  2. 多线程:同一个时刻,可以执行多个线程

    • 比如:QQ可以打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
  3. 并发:同一个时刻,多个任务交替执行,造成"貌似同时"的错觉,简单的说,单核CPU实现的多任务就是并发

  4. 并行:同一个时刻,多个任务同时执行。多核CPU可以同时执行

    • 也可能出现:并行和并发,并存在的情况

四、线程的基本使用

在Java中线程来使用有两种方法

  1. 基础Thread类,重写run方法

  2. 实现Runnable接口,重写run方法

案例一

![image-20220327105831422](!

)

package com.hspedu.threaduse;

/**
* @author DL5O
* @version 1.0
* 通过继承Thread 类创建线程
*/ public class Thread01 {
public static void main(String[] args) throws InterruptedException {
//创建Cat对象,可以当做线程使用
Cat cat = new Cat();
//启动线程,调用start的时候还调用run方法
cat.start();//最终执行 -> cat的run方法
//cat.run();
//如果这样写,run方法就是一个普通的方法,
// 是由主线程调用了,并没有真正的开线程,
// 会阻塞在这里,执行完毕后才会继续执行下面的代码 //说明:当main线程启动一个子线程thread-0,主线程不会阻塞,
// 即不会等待cat.start执行完毕后再往下执行
//主线程中如果后面还有代码的话,还会继续执行
//这时我们的主线程和主线程 是交替执行的
System.out.println("继续执行~~,"+Thread.currentThread().getName());
for (int i = 0; i < 60; i++) {
System.out.println("主线程 i=" + i);
//休眠1秒
Thread. sleep(1000);
}
}
} //1.当一个类继承了Thread 类,该类就可以当做一个线程使用
//2.我们会重写run方法,写上自己的业务逻辑
//3.run方法在Thread也是实现了Runnable 接口的run方法
class Cat extends Thread {
int times = 0; @Override
public void run() {//重写run方法,写上自己的业务逻辑
while (true) {
//每隔一秒钟,输出"喵喵,我是小猫咪"
System.out.println("喵喵,我是小猫咪" + (++times)
+ ",线程名称=" + Thread.currentThread().getName());
//让线程休眠1秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} if (times == 80) {
break;//当times 等于80就退出这个while循环,这时线程就退出了
}
}
}
}
  1. 当运行这个程序代码的时候,会创建一个进程并给他分配空间
  2. 会先执行main方法,开启一个main线程
  3. 当执行到car.start() 方法时会开启一个新的线程,这个线程是mian线程的子线程
    • 注意:

      • 这个car.start()是非阻塞的,即不会等到这个方法执行完后才继续往下执行
      • car.start()线程开启后,会去调用run方法!!
        • 但是如果直接写car.run(),这个时候则是用的主线程main线程去调用的,并没有开启新线程,执行玩这个方法后才会往下继续执行

执行过程

(1)
public synchronized void start() {
start0();
}
(2)
//start0() 是本地方法,是JVM调用,底层是c/c++实现的
//真正实现多线程的是start0(),而不是run方法
private native void start0();


案例二

实现runnable接口

说明:

  1. java是单继承的,在某些情况下已经继承了某个父类,这时继承Thread类方法来创建线程,显示是不可能的
  2. java设计者们提供了另外一个方式创建线程,就是通过实现Runnalb接口来创建线程

package com.hspedu.threaduse;

/**
* @author DL5O
* @version 1.0
* 通过实现接口Runnable 来开发线程
*/
public class Thread02 {
public static void main(String[] args){
Dog dog = new Dog();
//dog.start(); 这里不能调用start
//创建了Thread对象,把 dog对象,实现了Runnable,放入thread
Thread thread = new Thread(dog);
thread.start(); /*Tiger tiger = new Tiger();//实现了runnable接口
ThreadProxy threadProxy = new ThreadProxy(tiger);
threadProxy.start();
System.out.println("哈哈哈哈");*/
System.out.println("哈哈哈");
}
} class Animal{} class Tiger extends Animal implements Runnable{ @Override
public void run() {
System.out.println("老虎嗷嗷叫...");
}
} //模拟了最简的Thread类
class ThreadProxy implements Runnable{//可以当做ThreadProxy ,线程代理 private Runnable target = null;//属性,类型是Runnable @Override
public void run() {
if(target != null){
target.run();//进行动态绑定 运行类型是tiger,即实现了Runnable的类
}
} public ThreadProxy(Runnable target) {
this.target = target;
} public void start(){
start0();//这个方法是真正实现多线程的方法
} private void start0() {
run();
} } class Dog implements Runnable{ int count = 0;
@Override
public void run() {//普通方法,并没有启动线程
while(true){
System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} if(count == 10){
break;
}
} }
}
  • 把实现了runnable接口的对象传入到thread类,进行静态代理,调用start方法时,会去调用start0方法,start0方法还会调用对应该类的run方法,run方法又会去调用传入的对象的run方法,进行动态绑定

案例三

package com.hspedu.threaduse;

/**
* @author DL5O
* @version 1.0
* main 线程启动两个子线程
*/
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);//创建t1线程,用于输出hello,world
Thread thread2 = new Thread(t2);//创建t2线程,用于输出hi thread1.start();
thread2.start(); }
} class T1 implements Runnable {
int count = 0; @Override
public void run() {
while (true) {
System.out.println("hello,world " + (++count) + " "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} if (count == 10) {
break;
}
}
}
} class T2 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("hi " + (++count) + " " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 5) {
break;
}
}
}
}

  • 若还有线程没有执行完毕,那么主进程就不会退出

五、继承Thread vs 实现Runnable有什么区别

  1. 从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本
    质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
  2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了
    单继承的限制,建议使用Runnable
package com.hspedu.ticket;

/**
* @author DL5O
* @version 1.0
* 使用多线程,模拟三个窗口同时售票
* 总票数 100 张
*/
public class SellTicket {
public static void main(String[] args) {
//第一种方式
//测试
/*SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01(); sellTicket01.start();
sellTicket02.start();
sellTicket03.start();*/ System.out.println("===使用接口的方式来售票===");
SellTicket02 sellTicket02 = new SellTicket02();
Thread thread1 = new Thread(sellTicket02);
Thread thread2 = new Thread(sellTicket02);
Thread thread3 = new Thread(sellTicket02);
thread1.start();//第一个线程-窗口
thread2.start();//第二个线程-窗口
thread3.start();//第三个线程-窗口
}
} //使用第一种继承thread的方式
class SellTicket01 extends Thread{
private static int ticketNum = 100;//让多个线程共享 ticketNum
@Override
public void run() {
while (true){
if (ticketNum <= 0){
System.out.println("售票结束..");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票" + "剩余票数=" + (--ticketNum));
}
}
} //实现接口的方式
class SellTicket02 implements Runnable{
private int ticketNum = 100; @Override
public void run() {
while (true){
if (ticketNum <= 0){
System.out.println("售票结束..");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票" + "剩余票数=" + (--ticketNum));
}
}
}
  • 会出现超卖,和票数对不上的情况,这时候程序就会存在很大的问题=> 引出互斥等概念

六、线程的退出

基本说明:

  1. 当线程完成任务后,会自动退出
  2. 还可以通过变量来控制run方法退出的方式停止线程,即通知方式
package com.hspedu.exit_;

/**
* @author DL5O
* @version 1.0
*/
public class ThreadExitTest_ {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.start(); Thread.sleep(10*1000);
test.setLoop(false);
}
}
class Test extends Thread{
private boolean loop = true;
private int cnt = 0;
@Override
public void run() {
while(loop){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Test 执行中" + (++cnt));
}
System.out.println("Test 运行结束");
} public boolean isLoop() {
return loop;
} public void setLoop(boolean loop) {
this.loop = loop;
} public int getCnt() {
return cnt;
} public void setCnt(int cnt) {
this.cnt = cnt;
}
}

七、线程常用方法

常用方法第一组

  1. setName:给线程设置名称
  2. getName:返回线程的名称
  3. start:使线程开始执行
  4. run:线程调用的run方法
  5. setPriority:设置线程的优先级
  6. getPriority:获取线程的优先级
  7. sleep:让正在执行的线程休眠几秒
  8. interrupt:中断线程

  1. start底层会创建新的线程,会调用run,run本身不会启动新线程,不会启动新线程
  2. 线程优先级的范围
  3. interrupt,中断线程,但并没有真正结束线程。所以一般用于中断正在休眠线程
  4. sleep:线程的静态方法,使当前线程休眠

案例:

ThreadMethod01

package com.hspedu.method;

/**
* @author DL5O
* @version 1.0
*/
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.setName("大龙");
t.setPriority(Thread.MIN_PRIORITY);//设置最小的优先级
t.start();//启动子线程 //主线程打印5 个hi, 然后我就中断 子线程的休眠
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi " + (i+1));
} System.out.println(t.getName() + " 线程的优先级 = " + t.getPriority());//1
t.interrupt();//中断该线程,当执行到这里,就会中断,t线程的休眠 }
} class T extends Thread {//自定义的线程类 @Override
public void run() {
while (true) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 吃包子~~~" + (i + 1)); }
try {
System.out.println("大龙休息中~~");
Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
//InterruptedException 是捕获到一个终端异常
System.out.println(Thread.currentThread().getName() + "被 interrupt了");
}
}
}
}

常用方法第二组

注意:

  • 在某一个线程后使用了类名.join方法,那么会cpu会全部去执行 这个插队的线程,并且当这个线程全部执行完后,才会继续执行原来的线程
package com.hspedu.method;

/**
* @author DL5O
* @version 1.0
*/
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
int count = 0;
Test test = new Test();
test.start();
while (true){
System.out.println("hello " + (++count));
Thread.sleep(1000);
if(count == 5){
System.out.println("让test 线程先执行");
// test.join();//线程插队一定会成功
Thread.yield();//线程礼让 //让test 线程先执行,执行后可以看到,当执行join后,主线程就不再执行,
// 当这个test线程执行完毕后,主线程才继续执行
}
if(count == 20){
break;
}
} }
} class Test extends Thread{
private int count = 0;
@Override
public void run() {
while(true){
System.out.println("hi " + (++count));
try {
Thread.sleep( 1000);//休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 20){
break;
}
} }
}

课堂练习

package com.hspedu.method;

/**
* @author DL5O
* @version 1.0
*/
public class ThreadMethodExercise {
public static void main(String[] args) throws InterruptedException {
T01 t01 = new T01();
for (int i = 1; i <= 10; i++) {
System.out.println("hi " + i);
if (i == 5) {
Thread thread = new Thread(t01);
thread.start();
thread.join();
System.out.println("子线程结束...");
}
Thread.sleep(1000);
}
System.out.println("主线程结束...");
}
} class T01 implements Runnable{ @Override
public void run() { for (int i = 1; i <= 10 ; i++) {
System.out.println("hello " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

八、用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  3. 常见的守护线程:垃圾回收机制
package com.hspedu.method;

/**
* @author DL5O
* @version 1.0
* 守护线程的设置
*/
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
MyDaemonThread myDaemonThread = new MyDaemonThread();
Test01 test01 = new Test01();
myDaemonThread.setDaemon(true);
//如果我们希望当main线程结束后,子线程可以自动退出
//只需将子线程设为守护线程即可
myDaemonThread.start();
test01.start(); for (int i = 1; i <= 10; i++) {
System.out.println("我是主线程 " + i);
Thread.sleep(1000);
} }
} class MyDaemonThread extends Thread {
@Override
public void run() {
for (int i = 1; ; i++ ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是MyDaemonThread 守护线程 " + i);
}
}
} class Test01 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 15 ; i++ ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是Test01 用户线程 " + i);
}
}
}

总结:

  • 当所有的用户线程退出后,守护线程就会自动结束
  • 设置守护线程的方式:对象名.setDaemon(true)
  • 如果不进行设置,线程默认为用户线程

九、 线程的生命周期

线程状态转换图:


九、线程同步机制

关键字:Synchronized

  1. 在多线程编程,一些敏感数据不允许被多少个线程同时访问,此时就使用同步访问技术保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
  2. 也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

同步具体方法-Synchronized

  1. 同步代码块

    synchronized(对象){//获得对象的锁,才能操作同步代码
    //需要被同步代码;
    }
  2. synchronized还可以放在方法声明中,表示整个方法-为同步方法

    public synchronized void m(String name){
    //需要被同步的代码
    }
  3. 如何理解:

    • 就好像某个人要上厕所,上厕所前需要把门关上(上锁),完事之后再出来(解锁),那么其他小伙伴就可以在使用厕所了
  4. 使用synchronized解决售票

package com.hspedu.syn;

/**
* @author DL5O
* @version 1.0
* 使用多线程,模拟三个窗口同时售票
* 总票数 100 张
*/
public class SellTicket {
public static void main(String[] args) {
//第一种方式
//测试
/*SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01(); sellTicket01.start();
sellTicket02.start();
sellTicket03.start();*/ /*System.out.println("===使用接口的方式来售票===");
SellTicket02 sellTicket02 = new SellTicket02();
Thread thread1 = new Thread(sellTicket02);
Thread thread2 = new Thread(sellTicket02);
Thread thread3 = new Thread(sellTicket02);
thread1.start();//第一个线程-窗口
thread2.start();//第二个线程-窗口
thread3.start();//第三个线程-窗口*/ //测试
SellTicket03 sellTicket03 = new SellTicket03();
Thread thread1 = new Thread(sellTicket03);
Thread thread2 = new Thread(sellTicket03);
Thread thread3 = new Thread(sellTicket03);
thread1.start();//第一个线程-窗口
thread2.start();//第二个线程-窗口
thread3.start();//第三个线程-窗口
}
} //实现接口的方式,使用synchronized 实现线程同步
class SellTicket03 implements Runnable {
private int ticketNum = 100;
private boolean loop = true; public synchronized void sell() {
//同步方法,在同一个时刻只能有一个线程来执行我们的run方法
if (ticketNum <= 0) {
loop = false;
System.out.println("票已售空,售票结束..");
return;
} //休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票," + "剩余票数=" + (--ticketNum));
} @Override
public void run() { while (loop) {
sell();
}
}
} //使用第一种继承thread的方式
class SellTicket01 extends Thread {
private static int ticketNum = 100;//让多个线程共享 ticketNum @Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束..");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票" + "剩余票数=" + (--ticketNum));
}
}
} //实现接口的方式
class SellTicket02 implements Runnable {
private int ticketNum = 100; @Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束..");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票" + "剩余票数=" + (--ticketNum));
}
}
}

十、互斥锁

基本介绍:

  1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。

  2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

  3. 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问

  4. 同步的局限性:导致程序的执行效率要降低

  5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)

    • 非静态的这个锁是加在当前对象的
  6. 同步方法(静态的)的锁为当前类本身。

    • 静态方法的是加载当前类的

注意事项:

  1. 同步方法如果没有使用static修饰:默认锁对象是:this
  2. 如果方法使用了static修饰:默认是锁对象是:当前类.class
  3. 实现的落地步骤:
    • 需要先分析上锁的代码
    • 选择同步代码块或同步方法
    • 要求多个线程的锁对象为同一个即可!!!!
      • 即共享的资源上
      • 线程同步只会发生在共享同一个资源时
package com.hspedu.syn;

/**
* @author DL5O
* @version 1.0
* 使用多线程,模拟三个窗口同时售票
* 总票数 100 张
*/
public class SellTicket {
public static void main(String[] args) {
//第一种方式
//测试
/*SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01(); sellTicket01.start();
sellTicket02.start();
sellTicket03.start();*/ /*System.out.println("===使用接口的方式来售票===");
SellTicket02 sellTicket02 = new SellTicket02();
Thread thread1 = new Thread(sellTicket02);
Thread thread2 = new Thread(sellTicket02);
Thread thread3 = new Thread(sellTicket02);
thread1.start();//第一个线程-窗口
thread2.start();//第二个线程-窗口
thread3.start();//第三个线程-窗口*/ //测试
SellTicket03 sellTicket03 = new SellTicket03();
Thread thread1 = new Thread(sellTicket03);
Thread thread2 = new Thread(sellTicket03);
Thread thread3 = new Thread(sellTicket03);
thread1.start();//第一个线程-窗口
thread2.start();//第二个线程-窗口
thread3.start();//第三个线程-窗口
}
} //实现接口的方式,使用synchronized 实现线程同步
class SellTicket03 implements Runnable {
private int ticketNum = 100;
private boolean loop = true;
Object obj =new Object();//都是同一个对象 //同步方法(静态的) 的锁为当前类
//1.他的锁是加载我们这个类上的 SellTicket03.Class 上
//2.如果要在静态方法中实现一个同步代码块.
// synchronized (SellTicket03.class)
public synchronized static void m1(){ }
public static void m2(){
synchronized (SellTicket03.class){
System.out.println("m2");
}
} //是在方法上加的锁
//说明
//1.public synchronized void sell()就是一个同步方法
//2.这时锁是在this对象
//3.也可以在代码块上写 synchronized,同步代码块,互斥锁还是在this对象
public /*synchronized*/ void sell() {//同步方法,在同一个时刻只能有一个线程来执行我们的run方法
synchronized (/*this*/obj){
if (ticketNum <= 0) {
loop = false;
System.out.println("票已售空,售票结束..");
return;
} //休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票," + "剩余票数=" + (--ticketNum));
}
} @Override
public void run() { while (loop) {
sell();
}
}
} //使用第一种继承thread的方式
//new SellTicket01().start()
//new SellTicket01().start() this只对当前的对象有效
class SellTicket01 extends Thread {
private static int ticketNum = 100;//让多个线程共享 ticketNum //锁一般用于共享的只有
public void m1(){
synchronized (this){
System.out.println("hello");
}
} @Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束..");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票" + "剩余票数=" + (--ticketNum));
}
}
} //实现接口的方式
class SellTicket02 implements Runnable {
private int ticketNum = 100; @Override
public void run() {
while (true) {
if (ticketNum <= 0) {
System.out.println("售票结束..");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("窗口 " + Thread.currentThread().getName()
+ " 售出一张票" + "剩余票数=" + (--ticketNum));
}
}
}

十一、线程的死锁

基本介绍:

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程时一定要避免死锁的发生

package com.hspedu.syn;

/**
* @author DL5O
* @version 1.0
* 模拟线程死锁
*/ public class DeadLock {
public static void main(String[] args) {
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B线程");
A.start();
B.start();
}
} //线程
class DeadLockDemo extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
boolean flag; public DeadLockDemo(boolean flag){
this.flag = flag;
} @Override
public void run() {
//1.如果flag为T,线程就会先得到/持有 o1的对象锁,然后会尝试去得到o2的对先生
// 当持有到 o2的对象锁时,才会继续往下执行
//2.如果线程A 得不到o2的对象锁,就会Blocked
//3.如果flag 为 false,线程会去得到o2的对象锁,
//4.如果线程B 拿不到o1的对象锁,就会block
if(flag){
synchronized(o1){//对象互斥锁
System.out.println(Thread.currentThread().getName()+"进入1");
//拿到o1的锁之后会阻塞在这里,直到拿到o2的锁,
// 此时第二个线程thread2进入,拿到o2的锁,进入堵塞状态,因为一直拿不到o1的锁,
// o1又因为拿不到o2锁,释放不了,故成为了死锁
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"进行2");
}
}
}else{
synchronized (o2){ //第二个线程 o1的锁已经被拿了,
//这时,又想去拿o1的锁,但是o1在第36行阻塞到了,没有拿到o2的锁,没有释放,故这里也会被阻塞
//那么这时就会产生一种死锁的情况,要避免
System.out.println(Thread.currentThread().getName()+"进入3");
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"进行4");
}
}
}
}
}

十二、释放锁

下面操作会释放锁

  1. 当线程的同步方法、同步代码块执行结束
  2. 当前线程在同步代码块、同步方法中遇到break、return
  3. 当前线程在同步代码块、同步方法中出现了未处理的errorException,导致异常结束
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,也会释放锁

下面操作不会释放锁

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()Thread.yield()暂停当前线程的执行,不会释放锁

  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将会被挂起,该现场不会释放锁

    提示:应尽量避免使用suspend()resume()来控制线程,方法不再推荐使用了


作业

package com.hspedu.homework;

import java.util.Random;
import java.util.Scanner; /**
* @author DL5O
* @version 1.0
*/
public class Homework01 {
public static void main(String[] args) {
A a = new A();
B b = new B(a);
a.setName("A");
b.setName("B");
// a.setDaemon(true);
a.start();
b.start(); }
} class A extends Thread {
private int num;
static boolean loop = true;
private Random random; public A() {
random = new Random();
} public void showNum() {
//nextInt(int bound)
//返回伪随机的,均匀分布 int值介于0(含)和指定值(不包括),从该随机数生成器的序列绘制。
num = random.nextInt(101);//生成0~100的随机数
//num = (int)Math.random()*(100-0+1)+0;
System.out.println("随机数:" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Override
public void run() {
while (loop){
showNum();
}
}
} //用来控制A线程退出的线程B
class B extends Thread {
private String str;
private A a;
Scanner scanner; public B(A a) {
this.a = a;
scanner = new Scanner(System.in);
} @Override
public void run() {
synchronized (B.class) {
while (true) {
str = scanner.next();
if (str.equals("Q")) {
a.loop = false;
break;
}
}
}
}
}

package com.hspedu.homework;

/**
* @author DL5O
* @version 1.0
*/
public class Homework02 {
public static void main(String[] args) {
Withdraw withdraw = new Withdraw();
Thread A = new Thread(withdraw);
Thread B = new Thread(withdraw);
A.setName("A");
B.setName("B");
A.start();
B.start();
}
} class Withdraw implements Runnable {
private int money = 10000;
private boolean loop = true; public void deal() { //1.这里使用了synchronized 实现了线程同步
//2.当多个线程执行到这里的时候,就回去争夺 this对象锁
//3.那个线程执行到 this对象锁,就执行这个代码块,执行玩后,会释放这个锁,准备继续争夺
//4.争夺不到就阻塞到这里,就blocked
//5.this 是一个非公平锁 synchronized (/*Withdraw.class*/this){
if (money <= 0) {
loop = false;
System.out.println("余额不足");
return;
}
money -= 1000;
System.out.println(Thread.currentThread().getName() + "取走了1000,剩余" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Override
public void run() {
while (loop) {
deal();
}
}
}

JAVA 进程线程详解的更多相关文章

  1. “全栈2019”Java多线程第二十五章:生产者与消费者线程详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. IIS:连接数、并发连接数、最大并发工作线程数、应用程序池的队列长度、应用程序池的最大工作进程数详解

    Internet Information Services(IIS,互联网信息服务),是由微软公司提供的基于运行Microsoft Windows的互联网基本服务.最初是Windows NT版本的可选 ...

  3. IIS连接数、并发连接数、最大并发工作线程数、应用程序池的队列长度、应用程序池的最大工作进程数详解

    IIS:连接数.并发连接数.最大并发工作线程数.应用程序池的队列长度.应用程序池的最大工作进程数详解 iis性能指标的各种概念:连接数.并发连接数.最大并发工作线程数.应用程序池的队列长度.应用程序池 ...

  4. Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量

    Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程:    1.线程是一堆指令,是操作系统调度 ...

  5. Java synchronized 关键字详解

    Java synchronized 关键字详解 前置技能点 进程和线程的概念 线程创建方式 线程的状态状态转换 线程安全的概念 synchronized 关键字的几种用法 修饰非静态成员方法 sync ...

  6. JAVA IO 类库详解

    JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...

  7. 转:Java HashMap实现详解

    Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1.    HashMap概述:    HashMap是基于哈希表的M ...

  8. 通用线程:POSIX 线程详解,第 3 部分 条件互斥量(pthread_cond_t)

    使用条件变量提高效率 本文是 POSIX 线程三部曲系列的最后一部分,Daniel 将详细讨论如何使用条件变量.条件变量是 POSIX 线程结构,可以让您在遇到某些条件时“唤醒”线程.可以将它们看作是 ...

  9. Java类加载器详解

    title: Java类加载器详解date: 2015-10-20 18:16:52tags: JVM--- ## JVM三种类型的类加载器- 我们首先看一下JVM预定义的三种类型类加载器,当一个 J ...

随机推荐

  1. .NET 6学习笔记(2)——通过Worker Service创建Windows Service

    通过Visual Studio中的Windows Service模板,我么可以创建.NET Framework版本的Windows Service,网络上对此已有详细且丰富的各路教程.但在我们升级到. ...

  2. Redis(一):基本数据类型与底层存储结构

    最近在整理有关redis的相关知识,对于redis的基本数据类型以及其底层的存储结构简要的进行汇总和备注(主要为面试用) Redis对外提供的基本数据类型主要为五类,分别是 STRING:可以存储字符 ...

  3. 关于二维DP————站上巨人的肩膀

    意匠惨淡经营中ing, 语不惊人死不休........ 前几天学了DP,做了个简单的整理,记录了关于DP的一些概念之类的,今天记录一下刚学的一个类型 ----关于二维DP 那建立二维数组主要是干嘛用的 ...

  4. Rsync反弹shell

    vulhub环境靶机 : 192.168.91.130 攻击机:kali 192.168.91.128 一.环境搭建 vulhub环境靶机环境搭建 ​ 在纯净ubuntu中部署vulhub环境: 1. ...

  5. linux下安装简单的文件上传与下载工具 lrzsz

    编译安装 1.从下面的网站下载 lrzsz-1.12.20.tar.gz wget https://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz 2.查看里面的I ...

  6. leetcode-3无重复字符的最长子串

    题目原题: 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 ...

  7. Rabbit MQ 怎么保证可靠性、幂等性、消费顺序?

    RabbitMQ如何保证消息的可靠性 RabbitMQ消息丢失的三种情况 生产者弄丢消息时的解决方法 方法一:生产者在发送数据之前开启RabbitMQ的事务(采用该种方法由于事务机制,会导致吞吐量下降 ...

  8. 并发包中automic类的原理

    提到同步,我们一般首先想到的是lock,synchronized,但java中有一套更加轻量级的同步方式即atomic类.java的并发原子包里面提供了很多可以进行原子操作的类,比如: AtomicI ...

  9. 会话缓存(Session Cache)?

    最常用的一种使用 Redis 的情景是会话缓存(session cache).用 Redis 缓存会 话比其他存储(如 Memcached)的优势在于:Redis 提供持久化.当维护一个不 是严格要求 ...

  10. 什么是 spring bean?

    它们是构成用户应用程序主干的对象. Bean 由 Spring IoC 容器管理. 它们由 Spring IoC 容器实例化,配置,装配和管理. Bean 是基于用户提供给容器的配置元数据创建.