开发者社区> 游客wiepw7jj4edse> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

操作系统--

简介: -
+关注继续查看

线程概念

程序:就是应用程序,是静态的,通俗来讲就是我们写的代码,

进程:动态的程序,是程序的一次运行,也可以是进行中的程序,有自身的产生,存在,消亡

线程:线程是进程的实体,一进程可以多线程,比如qq的多个聊天窗口可以同时聊天

并发:多个任务交替执行,单核cpu实现多任务就是并发

并行:多个任务同时执行,并行是真正意义上的同步实现,多核cpu

区别:

每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。

进程是独立的,但线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。

image

线程状态

image

image

/**

 * @author 伍六七

 * @date 2022/8/1 15:30

 */

public class ThreadState {

    public static void main(String[] args) throws InterruptedException {

        T t = new T();

        System.out.println(t.getName()+"状态"+t.getState());

        t.start();

        while (Thread.State.TERMINATED!=t.getState()){

            System.out.println(t.getName()+"状态"+t.getState());

            Thread.sleep(500);

        }

        System.out.println(t.getName()+"状态"+t.getState());

    }

}

class T extends Thread{

    @Override

    public void run() {

        for (int i = 0; i < 3; i++) {

            System.out.println("执行次数"+i);

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}


线程常用方法

imageimage

线程优先级

每个线程都有优先级,优先级的高低只与线程获得执行机会的次数多少有关,并非是线程优先级越高的就一定先执行,因为哪个线程的先运行取决于CPU的调度,无法通过代码控制。

  • MAX_PRIORITY=10,最高优先级
  • MIN_PRIORITY=1,最低优先级
  • NORM_PRIORITY=5,默认优先级

相关方法

  • int getPriority() :返回线程的优先级
  • void setPriority(int newPriority) : 设置线程的优先级

主线程的默认优先级为5,如果线程A创建线程B,那么A与B是相同的优先级,但由于不同操作系统的设置不同,不建议自定义优先级

Thread方法

创建Thread方法

/**

* @author 伍六七

* @date 2022/8/1 13:56

*/

public class SellTicket {

    public static void main(String[] args) {

        //Thread

        SellTicket01 sellTicket01 = new SellTicket01();

        SellTicket01 sellTicket02 = new SellTicket01();

        SellTicket01 sellTicket03 = new SellTicket01();

        sellTicket01.start();

        sellTicket02.start();

        sellTicket03.start();

        

        //Runnable

        SellTicket02 sellTicket = new SellTicket02();

        new Thread(sellTicket).start();

        new Thread(sellTicket).start();

        new Thread(sellTicket).start();

        

        //lambda

        new Thread(() -> {

            System.out.println(1);

        },"sellTicket00").start();

    }

}


//使用Thread

class SellTicket01 extends Thread{

    @Override

    public void run() {

        System.out.println(1);

    }

}


//Runnable接口

class SellTicket02 implements Runnable{

    @Override

    public void run() {

        System.out.println(1);

    }

}

run()和start()

  • run():普通方法重写,会阻塞,执行完该方法才向下走。
  • start():启动线程->最终执行run方法。

调用run方法并没有开启新的线程,它仍旧是在主线程里

start()形成子线程原理

image

start0()是本地方法,JVM机调用

image

sleep() 方法和 wait()

先回顾一下线程休眠

线程休眠:让运行中的的线程暂停一段时间,进入计时等待状态

  • 调用sleep后,当前线程放弃CPU,进入计时等待状态,但仍然占用该cpu资源,在指定时间段之内,该线程不会获得执行的机会,此状态下的线程不会释放同步锁/同步监听器
  • wait是进入等待池等待,让出系统资源,其他线程可以占用cpu,一般wait不会加时间限制。

sleep方法和yield方法的区别:

  • 共同点是都能使当前处于运行状态的线程放弃CPU时间片,把运行的机会给其他线程;
  • 不同点在于:sleep方法会给其他线程运行机会,但是并不会在意其他线程的优先级;而yield方法只会给相同优先级或者更高优先级的线程运行的机会;
  • 调用sleep方法后,线程进入计时等待状态,而调用yield方法后,线程进入就绪状态

yeild() 方法和join()

  • yeild():当线程A的yield方法执行后,线程不会释放锁,而是释放了CPU的执行权,将当前执行的线程重新变为就绪状态,让CPU重新选择要执行的线程,也有可能当前线程执行完yield方法后,CPU又一次选中这个线程执行。该方法主要用于调试或者测试,比如在多线程竞争条件下,让错误重现现象或者更加明显。
  • join():当线程A调用线程B的join,那么系统会在此时将A置于等待状态先执行线程B,等到线程B执行完成后再开始继续执行线程A

image

用户线程与守护线程

用户线程与守护线程也被称为前台线程与后台线程

守护线程会伴随用户线程的结束而结束,其目的是为其他线程提供服务常见的:垃圾处理机制

设置线程为守护线程的方法,该方法必须在start方法之前调用,否则会触发IllegalThreadStateException异常,因为线程一旦启动,就无法对其做修改了。

  • .setDaemon(true);

/**

 * @author 伍六七

 * @date 2022/8/1 14:31

 */

public class ThreadMethod_join {

    public static void main(String[] args) throws InterruptedException {

        ThreadTest01 threadTest01 = new ThreadTest01();

        threadTest01.setDaemon(true);

        threadTest01.start();

        for (int i = 0; i < 5; i++) {

            Thread.sleep(50);

            System.out.println("主线程");

        }

    }

}

class ThreadTest01 extends Thread{

    @Override

    public void run() {

        int i = 10;

        while (i-->0) {

            try {

                Thread.sleep(50);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            String name = Thread.currentThread().getName();

            System.out.println(name+"执行了");

            if(i==4){

                Thread.yield();

            }

        }

    }

}

如何预防和避免线程死锁?

售票员-资源抢夺冲突

image

package com.wuliuqi.ticket;


/**

 * @author 伍六七

 * @date 2022/8/1 13:56

 */

public class SellTicket {

    public static void main(String[] args) {

        //Thread

        SellTicket01 sellTicket01 = new SellTicket01();

        SellTicket01 sellTicket02 = new SellTicket01();

        SellTicket01 sellTicket03 = new SellTicket01();

        sellTicket01.start();

        sellTicket02.start();

        sellTicket03.start();

}

class SellTicket01 extends Thread{


    private static int ticketNum = 100;

    @Override

    public  void run() {

        while (ticketNum>0) {

            try {

                Thread.sleep(50);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

        }

    }

}

超卖问题-没票时return

if(ticketNum<=0){

    Loop=false;

    System.out.println("没票了");

    return;

}

synchronized解决资源冲突

注意用implements Runnable,extends Thread会重复

//用synchronized修饰方法

//不要修饰run方法,否则会一个线程买完所有票

public synchronized void Sell() 

情况1-成功

image

修饰Sell方法,在run方法执行被修饰的Sell方法,解决了资源冲突

class SellTicket02 implements Runnable{

    private static int ticketNum = 100;

    boolean Loop = true;

    public synchronized void Sell(){

        if(ticketNum<=0){

            Loop=false;

            System.out.println("没票了");

            return;

        }

        System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

    }

    @Override

    public  void run() {

        while (Loop){

            Sell();

            try {

                Thread.sleep(10);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

情况2-独享

image

修饰run方法,实现了票的冲突,但是被独享了

class SellTicket02 implements Runnable{


    private static int ticketNum = 100;

    boolean Loop = true;

    @Override

    public synchronized void run() {

        while (Loop){

            if(ticketNum<=0){

                Loop=false;

                System.out.println("没票了");

                return;

            }

            System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

            try {

                Thread.sleep(10);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

synchronized修饰方法-

//修饰方法

//对象锁

private static int ticketNum = 100;

boolean Loop = true;

public synchronized void Sell(){//synchronized修饰方法

    if(ticketNum<=0){

        Loop=false;

        System.out.println("没票了");

        return;

    }

    System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

}

//用对象锁this

public void Sell(){

    synchronized (this){

        if(ticketNum<=0){

            Loop=false;

            System.out.println("没票了");

            return;

        }

        System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

    }

}

//用引用锁Object

private static int ticketNum = 100;

boolean Loop = true;

private static Object lock=new Object(); //必须是静态的。

public void Sell(){

    synchronized (lock){//synchronized(object)

        if(ticketNum<=0){

            Loop=false;

            System.out.println("没票了");

            return;

        }

        System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

    }

}

//用类锁class

public class SynchronizeMain {

}

class test{

    public void Sell(){

        synchronized (SynchronizeMain.class){//写类

            if(ticketNum<=0){

                Loop=false;

                System.out.println("没票了");

                return;

            }

            System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

        }

    }

}

实例锁--引用锁(常用,多个对象同一把锁)--类锁


对象就是实例一个类可以有多个实例,每个实例都可以有自己的锁

同步局限性:执行效率降低

一个实例只能有一个锁对象,只能修饰方法和代码块。所以属于对象的,并且可以加锁的就是非静态方法和非静态代码块。

为什么要用 synchronized (object) 这种方式,去多创一个object对象

引用锁就是说在类中创建一个任意类型的引用,假如说你创建一个静态的,那么这个引用属于类本身,使用这个引用加锁,就相当于使用类加锁的效果,假如你创建一个非静态的引用,那么这个引用属于实例,每个实例都有,那么使用这个引用加锁,就相当于使用对象锁加锁的效果。

  • 多线程对类的同一个实例的方法的同步,都属于对象锁的范畴。
  • -----------------------------------------------------------
  • 实例锁synchronized(this),锁的对象是this,是类的实例。
  • 引用锁synchronized(object),锁的对象是object。
  • 类锁sycronized(Account.class),加在类对象上.当sell加了sycronized(Account.class),则Account实例对象a和b调用sell(),都需要得到同一把Account类锁
  • -----------------------------------------------------------
  • 若线程A持有了Account类锁,线程B又去持有a实例的实例锁,这是允许的并不冲突
  • syncronized修饰实例方法和代码块加锁的是当前实例对象this,静态方法加锁的是类class.

原因在于,synchronized(this)的锁对象是this,如果使用这种方式,一旦锁对象(实例)被别人获取,别人只要开个线程进行死循环 ,那你的Thread 1,这个正常的工作线程,就永远得不到执行,造成死锁。synchronized(object),锁的对象是object。所以,你拿到了sTest的实例锁,也不会影响到Thread1的正常执行。

/**

 * @author 伍六七

 * @date 2022/8/1 18:44

 */

//方式二--this---无输出

class STest{

    public void print(){

        synchronized (this){

            System.out.println("xxxx");

        }

    }

}

//方式一--Object---成功输出

class STest{

    private final Object object = new Object();

    public void print(){

        synchronized (object){

            System.out.println("xxxx");

        }

    }

}

public class SynchronizeMain {


    public static void main(String[] args) throws InterruptedException {


        STest sTest = new STest();


        // Thread 1

        Thread t1 = new Thread(() -> {

            sTest.print();

        });


        // Thread 2

        Thread t2 = new Thread(() -> {


            try {  //这里拿到的是实例锁

                synchronized (sTest){//当STest是引用锁时,采用反射使用sTest的同一把锁

                    while (true);

                }

            } catch (Exception e) {

                System.out.println("Exception="+e.getMessage());

            }


        });


        t2.start();

        Thread.sleep(1000);

        t1.start();

    }

}

案例

卖票

public class ExerciseSell {

    public static void main(String[] args) {

        TicketWindow ticketWindow = new TicketWindow(2000);

        List<Thread> list = new ArrayList<>();

        // 用来存储买出去多少张票

        List<Integer> sellCount = new Vector<>();

        for (int i = 0; i < 2000; i++) {

            Thread t = new Thread(() -> {

                // 分析这里的竞态条件

                int count = ticketWindow.sell(randomAmount());

                sellCount.add(count);

            });

            list.add(t);

            t.start();

        }

        list.forEach((t) -> {

            try {

                t.join();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        });

        // 买出去的票求和

        log.debug("selled count:{}",sellCount.stream().mapToInt(c -> c).sum());

        // 剩余票数

        log.debug("remainder count:{}", ticketWindow.getCount());

    }

    // Random 为线程安全

    static Random random = new Random();

    // 随机 1~5

    public static int randomAmount() {

        return random.nextInt(5) + 1;

    }

}


class TicketWindow {

    private int count;

    public TicketWindow(int count) {

        this.count = count;

    }

    public int getCount() {

        return count;

    }

    public int sell(int amount) {

        if (this.count >= amount) {

            this.count -= amount;

            return amount;

        } else {

            return 0;

        }

    }

}


转账

public class ExerciseTransfer {

    public static void main(String[] args) throws InterruptedException {

        Account a = new Account(1000);

        Account b = new Account(1000);

        Thread t1 = new Thread(() -> {

            for (int i = 0; i < 1000; i++) {

                a.transfer(b, randomAmount());

            }

        }, "t1");

        Thread t2 = new Thread(() -> {

            for (int i = 0; i < 1000; i++) {

                b.transfer(a, randomAmount());

            }

        }, "t2");

        t1.start();

        t2.start();

        t1.join();

        t2.join();

        // 查看转账2000次后的总金额

        log.debug("total:{}",(a.getMoney() + b.getMoney()));

    }

    // Random 为线程安全

    static Random random = new Random();

    // 随机 1~100

    public static int randomAmount() {

        return random.nextInt(100) +1;

    }

}

class Account {

    private int money;

    public Account(int money) {

        this.money = money;

    }

    public int getMoney() {

        return money;

    }

    public void setMoney(int money) {

        this.money = money;

    }

    public void transfer(Account target, int amount) {

        if (this.money > amount) {

            this.setMoney(this.getMoney() - amount);

            target.setMoney(target.getMoney() + amount);

        }

    }

}


死锁


jconsole

imageimage

因此注意:主线程结束,进程不一定结束(子线程还在运行)

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【操作系统】操作系统引论
【操作系统】操作系统引论
67 0
【操作系统】操作系统接口
【操作系统】操作系统接口
62 0
人的操作系统
我们的大脑里都有各自的操作系统,它需要我们自己主动地、持续地升级。我们的大脑里都有各自的操作系统,它需要我们自己主动地、持续地升级。 “操作系统”这个概念,最早从李笑来老师的《新生--七年就是一辈子》看到。
1007 0
操作系统——概论
  进入二十世纪之后,随着各种电子产品的风起云涌,电脑也普及到每家每户,除了家里一些老人对电脑操作还不太熟练之外,几乎所有的年轻人都可以熟练地使用电脑。
879 0
操作系统随想
        我们每天都在与计算机打交道,它改变了整个人类的生活。打开电脑后,我们想进入哪个主题,只需用鼠标点击一下相应的图标即可。这真是太方便了!但是你有没有想过,为什么计算机会那么听话,总是乖乖地完成你给它布置的任务,虽然偶尔也会“罢工”?在你实现超级体验的过程中,它又做了些什么?以上这些便捷,都要归功于一个被称为操作系统的软件层。
731 0
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载