操作系统--

简介: -

线程概念

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

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

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

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

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

区别:

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

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

线程状态

/**

* @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();

           }

       }

   }

}


线程常用方法

线程优先级

每个线程都有优先级,优先级的高低只与线程获得执行机会的次数多少有关,并非是线程优先级越高的就一定先执行,因为哪个线程的先运行取决于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()形成子线程原理

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

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

用户线程与守护线程

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

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

设置线程为守护线程的方法,该方法必须在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();

           }

       }

   }

}

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

售票员-资源抢夺冲突

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-成功

修饰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-独享

修饰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

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

目录
相关文章
|
5月前
操作系统的主要功能
操作系统的主要功能
|
人工智能 JavaScript 前端开发
「操作系统」1. 基础
「操作系统」1. 基础
|
7月前
|
Linux Android开发 iOS开发
操作系统的作用与常见操作系统介绍
操作系统的作用与常见操作系统介绍
|
Linux 编译器 C语言
【Linux:程序地址空间--原来操作系统也喜欢画大饼】
【Linux:程序地址空间--原来操作系统也喜欢画大饼】
95 0
|
存储 缓存 安全
【Linux】操作系统&&进程概念
冯诺依曼体系结构、操作系统的概念、进程的概念、创建和杀死进程、创建子进程。
|
安全 Linux C语言
【Linux】初步理解操作系统和进程概念
【Linux】初步理解操作系统和进程概念
100 0
|
存储 缓存 安全
【Linux】操作系统与进程的概念(一)
【Linux】操作系统与进程的概念
148 0
【Linux】操作系统与进程的概念(一)
|
Linux Shell
【Linux】操作系统与进程的概念(二)
【Linux】操作系统与进程的概念
128 0
【Linux】操作系统与进程的概念(二)
|
存储 算法 调度