操作系统--

简介: -

线程概念

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

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

线程:线程是进程的实体,一进程可以多线程,比如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

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

目录
相关文章
|
存储 域名解析 负载均衡
负载均衡是什么,负载均衡有什么作用
负载均衡是什么,负载均衡有什么作用
|
NoSQL MongoDB 数据库
MongoDB日志浅析
MongoDB 日志
7225 0
|
4月前
|
机器学习/深度学习 人工智能 运维
运维别再“救火队”了,智能异常检测才是未来!
运维别再“救火队”了,智能异常检测才是未来!
307 79
|
负载均衡 算法
架构学习:7种负载均衡算法策略
四层负载均衡包括数据链路层、网络层和应用层负载均衡。数据链路层通过修改MAC地址转发帧;网络层通过改变IP地址实现数据包转发;应用层有多种策略,如轮循、权重轮循、随机、权重随机、一致性哈希、响应速度和最少连接数均衡,确保请求合理分配到服务器,提升性能与稳定性。
2542 11
架构学习:7种负载均衡算法策略
|
7月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
510 5
|
监控 API
【zookeeper 第四篇章】监控 Watcher
ZooKeeper通过Watcher机制实现了数据的发布/订阅功能。多个订阅者可以监听同一主题对象,一旦该对象状态变化,如节点内容或子节点列表变动,ZooKeeper会实时通知所有订阅者。Watcher架构包括ZooKeeper服务端、客户端及其Watcher管理器。客户端向服务端注册Watcher并保存至本地管理器中;当状态变化时,服务端通知客户端,触发相关Watcher回调处理逻辑。
389 2
|
负载均衡 监控 算法
|
Java UED
基于SpringBoot自定义线程池实现多线程执行方法,以及多线程之间的协调和同步
这篇文章介绍了在SpringBoot项目中如何自定义线程池来实现多线程执行方法,并探讨了多线程之间的协调和同步问题,提供了相关的示例代码。
4433 0
|
JavaScript 索引 前端开发
9.【TypeScript 教程】接口(Interface)
9.【TypeScript 教程】接口(Interface)
251 4
|
存储 算法 数据处理
数据结构与云计算:实现高效的数据存储与处理
本文探讨了数据结构和云计算在现代信息技术中的核心作用。数据结构,包括线性与非线性结构,影响着程序的效率,而在云计算环境中,这些结构需要适应分布式、并行和高可用性的需求。云计算提供弹性、可扩展的计算资源,分为IaaS、PaaS和SaaS三层服务模式。数据存储与处理在云计算中面临优化,如分布式数据存储利用哈希表实现数据分布,分布式数据处理采用映射减少算法提高效率,同时数据压缩和加密确保存储节省与安全性。未来,云计算将继续发展,面临扩展性、可靠性和安全性的挑战,而数据结构的优化将是提升系统性能的关键。
1320 5