多线程基础知识(下)

目录

1.线程终止

2.线程的常用方法

3.守护线程

4.线程的生命周期

5.Synchronized关键字

6.互斥锁

7.线程死锁

8.释放锁


1.线程终止

线程终止即停止执行此线程

线程终止分为两种情况:

一种是等待线程执行完后 它会自动终止

另一种是通过使用变量来控制run方法退出的方式来终止线程 即通知方式

我们来看看第二种方式的具体实现

public class Pre04 {
    public static void main(String[] args) throws InterruptedException {
        test04 test04 = new test04();
        test04.start();
        //主线程
        for (int i = 0; i <10; i++) {
            Thread.sleep(1000);
            System.out.println("主线程"+i);
        }
        test04.setLoop(false);
        while(true){
            System.out.println("主线程继续执行");
        }

    }
}
class test04 extends Thread{
    boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    int counts = 0;
    @Override
    public void run() {
        while(loop){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程"+counts++);
        }
    }
}

执行效果如下

主线程7
主线程8
Thread-0线程8
Thread-0线程9
主线程9
主线程继续执行
主线程继续执行

在主线程9输出后 后续不再执行Thread-0线程 成功终止了Thread-0线程

2.线程的常用方法

public class Pre04 {
    public static void main(String[] args) throws InterruptedException {
        test04 test04 = new test04();
        //1.给线程起名字
        test04.setName("hsp老师讲的太好了");
        //2.设置线程优先级
        test04.setPriority(Thread.MAX_PRIORITY);//高优先级 低优先级 中等优先级
        test04.start();

        int counts = 0;
        //主线程
        for (int i = 0; i <10; i++) {
            Thread.sleep(1000);
            System.out.println("主线程"+i);
        }
        //3.打断子线程休眠
        test04.interrupt();
        //4.yield线程的礼让
       // Thread.yield();

    }
}
class test04 extends Thread{

    int counts = 0;
    @Override
    public void run() {

            try {
                Thread.sleep(100000);//100s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程"+counts++);
        }
    }

前三个 起名字 就是给线程重命名   设置线程优先级 优先级高的更容易被执行(概率) 打断休眠就是让子线程跳过休眠状态 然后运行

public class Pre04 {
    public static void main(String[] args) throws InterruptedException {
        test04 test04 = new test04();
        test04.start();
        int counts = 0;
        //主线程
        for (int i = 0; i <10; i++) {
            Thread.sleep(1000);
            System.out.println("主线程"+i);
            if(i==5){  
                //5.join 线程插队
                test04.join();
            }
        }
        //4.yield线程的礼让
       Thread.yield();
    }
}
class test04 extends Thread {

    int counts = 0;

    @Override
    public void run() {
        while (counts < 10) {
            try {
                Thread.sleep(1000);//100s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "线程" + counts++);
        }
    }
}

最后两个 一个是插队 一个是礼让 礼让不一定成功 礼让的含义是如果只有一个厕所 我就让给你 但是事实上可能有多个厕所  所以不一定成功  插队是指 老板来了 必须得让他先上  在程序上体现为插队的线程全部执行完了 再回过头执行原线程

运行效果

Thread-0线程3
Thread-0线程4
主线程4
Thread-0线程5
主线程5//到五主线程停止输出 被插队
Thread-0线程6
Thread-0线程7
Thread-0线程8
Thread-0线程9//Thread-0全部执行完毕 让给主线程
主线程6
主线程7

3.守护线程

1.用户线程:也叫工作线程 以线程的任务执行完或通知方式结束 

2.守护线程:一般是为工作线程服务的 当所有的用户线程结束后 守护线程自动结束 例如 最典型的就是垃圾回收线程

public class Pre05 {
    public static void main(String[] args) throws InterruptedException{
        test05 test05 = new test05();
        test05.setDaemon(true);//设置为守护线程
        test05.start();
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            System.out.println("主线程"+i);
        }

    }
}
class test05 extends Thread{
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程执行");
        }
    }
}

执行效果如下

子线程执行
主线程0
子线程执行
主线程1
子线程执行
主线程2
主线程3
子线程执行
子线程执行
主线程4
主线程5
子线程执行
子线程执行
主线程6
子线程执行
主线程7
子线程执行
主线程8
子线程执行
主线程9

 当主线程(用户线程)运行结束后 子线程(守护线程)跟着结束

4.线程的生命周期

    • NEW
      尚未启动的线程处于此状态。
    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态。
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
      正在等待另一个线程执行特定动作的线程处于此状态。
    • TIMED_WAITING
      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
    • TERMINATED
      已退出的线程处于此状态。
public class Thread10 {
    public static void main(String[] args) throws  InterruptedException{
        R r = new R();
        System.out.println(r.getName()+"状态"+r.getState());
        r.start();
        while(Thread.State.TERMINATED!=r.getState()){
            System.out.println(r.getName()+"状态"+r.getState());
            Thread.sleep(1000);
        }
        System.out.println(r.getName()+"状态"+r.getState());;

    }

}


class R extends Thread{
    @Override
    public void run() {
    while (true){
        for (int i = 0; i < 10; i++) {
            System.out.println("hi"+i);
            try {
                Thread.sleep(1000);//Thread-0状态TIMED_WAITING
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        break;
    }
    }
}

目前了解就好 后面会深挖  可以跑下下面的程序体会线程的不同状态

5.Synchronized关键字

前面提到过多线程卖票 有可能会超卖 即当只剩最后一张票时 有可能三个线程同时进入 这样会导致超卖问题 为了解决这个问题呢  我们引入了线程同步机制

线程同步机制

1.在多线程编程 一些敏感数据不允许被多个线程同时访问 此时就使用同步访问技术 保证数据在任何时刻 最多有一个线程访问 以保证数据的完整性  

2.也可以这样理解:线程同步 即当有一个线程在对内存进行操作时 其他线程都不可以对这个内存地址进行操作 直到该线程完成操作 其他线程才能对该内存地址进行操作 即每次只能有一个线程访问

同步具体方法-Synchronized

两种形式

第一种synchronized同步代码块

public class Pre06 {
    public static void main(String[] args) {
        test06 test060 = new test06();
        test06 test061 = new test06();
        test06 test062 = new test06();
        test060.start();
        test061.start();
        test062.start();
    }
}
class test06 extends Thread{
    private static int num = 100;
    @Override
    public void run() {
        synchronized (this) {
            while (num > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "线程卖出一张票,还剩" + num-- + '张');
            }
        }
    }
}

卖票问题 此时不会超发   

第二种 同步方法

public class Pre06 {
    public static void main(String[] args) {
        test06 test060 = new test06();
        test06 test061 = new test06();
        test06 test062 = new test06();
        test060.start();
        test061.start();
        test062.start();
    }
}
class test06 extends Thread{
    private static int num = 100;
    @Override
    public  void run() {
        make();
    }
    public synchronized void make(){
        while (num > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "线程卖出一张票,还剩" + num-- + '张');
        }
    }
    }

6.互斥锁

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

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

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

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

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

Object object = new Object();//三个线程操作的是同一个对象
    @Override
    public  void run() {
        synchronized (object){
        //必须是一个对象

6.同步方法(静态的)锁是当前类本身

  public synchronized static void kush(){
       //锁加在类上
   }
   public static void m2(){
       synchronized (A1.class){//加在类本身上
           System.out.println("shjaj");
       }
   }

7.线程死锁

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

比如  一个很生活化的例子

妈妈:你做完作业 我才让你玩手机

小明:我先玩手机 才能写作业 (hhh)

模拟死锁的过程

public class Thread11 {
    public static void main(String[] args) {
        //模拟死锁
        DeadLock deadLock = new DeadLock(true);
        DeadLock deadLock1 = new DeadLock(false);
        deadLock.start();
       deadLock1.start();

    }
}

class DeadLock extends Thread{
 //保证多线程共用一个对象
    //如果flag为T 先拿到o1对象锁 再尝试获取o2对象锁
    //如果线程A得不到o2对象锁 就会Blocked
    //如果flag为F 线程B就会先拿到o2对象锁 再尝试获取o1对象锁
    //如果线程B得不到o1对象锁 就会Blocked
    static Object o1 = new Object();
    static Object o2 = new Object();
    boolean flag;

    public DeadLock( boolean flag) {
        this.flag = flag;
    }
    @Override
    public void run() {
        if(flag){
            synchronized (o1){//对象互斥锁
                System.out.println(Thread.currentThread().getName()+"进入1");
                synchronized (o2){//这里获得对象的监视权
                    System.out.println(Thread.currentThread().getName()+"进入2");
                }
            }
        }
        else{
            synchronized (o2){
                System.out.println(Thread.currentThread().getName()+"进入3");
                synchronized (o1){
                    System.out.println(Thread.currentThread().getName()+"进入4");
                }
        }
    }
}}

8.释放锁

1.当前线程的同步方法 同步代码块执行结束

2.当前线程在同步代码块 同步方法中遇到break return

3.当前线程在同步代码块 同步方法中出现了 未处理的Error或者Exception 导致异常结束

4.当前线程在同步代码块 同步方法中执行了线程对象的wait方法 当前线程暂停 并且释放锁

1.线程执行同步代码块或者同步方法时 程序调用Thread.sleep()和yield方法不会释放锁

2.线程执行同步代码块时 其他线程调用了该线程的suspend方法将该线程挂起 该线程不会释放锁 应尽量避免这种情况

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值