Java多线程入门(一)[云图智联]

    技术2023-04-04  98

    免费学习视频欢迎关注云图智联:https://e.yuntuzhilian.com/

    Java多线程入门 线程同步 为什么要线程同步? 使用多线程可以让我们的程序更加充分的利用CPU,提高程序的效率,但是同时也带来了一些问题。多线程在使用线程公共资源的时候往往会遇到问题。 想象一下这样一个情景:一个商店中有三件商品,但是四个顾客同时来购买商品,他们都能买到商品吗?

     

    我们用代码模拟一下:

    public class Test{        public static void main(String[] args) {              //创建一个商店              Store store = new Store();              //创建四个共用一个商店的顾客线程              CustomerThread ct1 = new CustomerThread("顾客1",store);              CustomerThread ct2 = new CustomerThread("顾客2",store);              CustomerThread ct3 = new CustomerThread("顾客3",store);              CustomerThread ct4 = new CustomerThread("顾客4",store);              //启动四个线程              ct1.start();              ct2.start();              ct3.start();              ct4.start();        } }  //商店类 class Store {        //剩余商品数量        static int goodsNumble = 3;        public Store(){        }        //出售一件商品  void sell() {         //先判断商品是否售完              if(goodsNumble<=0)                     System.out.println("商店:出售失败,商品卖完了");              else {                     System.out.println("此时还有"+goodsNumble+"件商品");                     goodsNumble--;                     System.out.println("售出一件商品,商品数量减1");                     }        } } class CustomerThread extends Thread{        private Store store;        public CustomerThread(String name,Store store) {              // TODO 自动生成的构造函数存根              setName(name);              this.store = store;        }        @Override        public void run() {              buy();        }        //购买商品,调用store的sell方法        public void buy() {              store.sell();        } } 运行结果

    从上面的代码和运行结果我们可以看到,因为顾客几乎是同时到达商店的,所以他们到的时候商品的水量都是3,于是商店给每一位顾客都出售了一件商品。这显然不是我们想要的结果。然后我们给Store类的sell()方法加上synchronized关键字,局部修改如下:

    运行结果:

    可以看到在我们给sell方法使用了synchronized关键字后Stroe会按照一定的顺序来为顾客服务,这就是synchronized方法的效果.当当前线程使用synchronized方法时,会给当前对象(store)上锁,上锁后其他线程在使用相同对象的方法时进入阻塞,需等待此线程中的该方法结束,才能继续运行。

    线程同步的三个方法 为了介绍线程同步的三个方法,我们先来看一下这样一个场景:有一个房间,房间里居住着四个人,四个人共用着房间里的一部电话。每个人都可以在任何时候使用电话,但是如果他们在同一时间都来使用这部电话又会发生什么呢?

     

    我们用代码模拟一下:

    public class SynchronizedMethod {        public static void main(String[] args) {              Room r = new Room();              //创建并启动4个学生线程              StudentThread s1  =new StudentThread("s1",r);              StudentThread s2  =new StudentThread("s2",r);              StudentThread s3  =new StudentThread("s3",r);              StudentThread s4  =new StudentThread("s4",r);              s1.start();              s2.start();              s3.start();              s4.start();        } }

    class Room {        public Phone phone;        public Room() {              // TODO 自动生成的构造函数存根              phone = new Phone();        }        }

    class Phone{        static final int FREE = 1,BUSY = 0;        //电话的状态        private int status=FREE;        public Phone() {                }        //判断电话是否空闲        public boolean  isFree() {              return status==FREE;        }        //打电话,半秒后挂断,为了打印电话使用者的名字,我们传入一个参数        public void call(String user) {              if(!isFree()) {                     System.out.println("有人正在使用电话哦");                     return;              }              status = BUSY;              System.out.println(user+"打出了电话...");              try {                     Thread.sleep(500);                     hangUp(user);              } catch (InterruptedException e) {                     // TODO 自动生成的 catch 块                     e.printStackTrace();              }        }        //挂电话        public  void hangUp(String user) {              status = Phone.FREE;              System.out.println(user+"挂断了电话...");        } }

    class StudentThread extends Thread{        private Room room;        public StudentThread(String name,Room room) {              setName(name);              this.room = room;        }        @Override        public void run() {                     this.room.phone.call(getName());        } }

    运行结果

    我们可以发现当线程s1使用了对象phone的call方法的时候,其他三个线程仍然使用了call方法。这就是线程不同步的效果。接下来我们分别用三种方法实现线程同步。

    synchronized方法 将Phone类的call声明为sychronized方法(在call方法前面加上关键字即可),修改部分代码如下: 运行结果

    synchronized块 在call函数内添加synchronized代码块,此方法与上一方法相比,优点在于你不需将整个方法都变成同步的,只需把关键代码块变为同步即可,修改部分代码如下: 运行结果

    Lock 运行结果

    线程死锁 线程死锁,是指多个线程在使用公共资源时各占了资源的一部分,导致每个线程都等待其他线程释放资源才能完成工作,最终导致每个线程都无法完成工作,互相卡死。 想象一下这样的情景:上学时,你和你的同桌共用一个笔记本和一支钢笔,只是你的老师讲到了一个要点,于是你顺势抢到了钢笔,而你的同桌拿到了笔记本,谁也不肯让步,于是谁也无法完成笔记。 我们用代码模拟一下:

    public class DeadLock {        public static void main(String[] args) {              Stationery stationery = new Stationery();              //p1 takeNotes时先获取并锁定本子,然后再试图获取笔              Person p1 = new Person("p1",stationery) {                     @Override                     public void takeNotes() {                            synchronized (stationery.notebook) {                                  getNotebook();                                  synchronized (stationery.pen) {                                         getPen();                                  }                            }                     }              };

                 //p2 takeNotes时先获取并锁定笔,然后再试图获取本子              Person p2 = new Person("p2",stationery) {                     @Override                     public void takeNotes() {                            synchronized (stationery.pen) {                                  getPen();                                  synchronized (stationery.notebook) {                                         getNotebook();                                  }                            }                     }              };              p1.start();              p2.start();        } }

    //文具类(共用资源) class Stationery{        //笔        public static Pen pen = new Pen();        //笔记本        public static Notebook notebook= new Notebook(); }

    class Pen{         }

    class Notebook{

    } abstract class Person extends Thread{        Stationery stationery;        public Person(String name,Stationery s) {              // TODO 自动生成的构造函数存根              setName(name);              stationery = s;        }        //获取笔        Pen getPen() {                     System.out.println(getName()+":我拿打笔了");                     try {                            Thread.sleep(1);                     } catch (InterruptedException e) {                            // TODO 自动生成的 catch 块                            e.printStackTrace();                     }              return stationery.pen;        };        //获取本子        Notebook getNotebook() {                     System.out.println(getName()+":我拿打笔记本了");                     try {                            Thread.sleep(1);                     } catch (InterruptedException e) {                            // TODO 自动生成的 catch 块                            e.printStackTrace();                     }              return stationery.notebook;        };        //抽象方法         public abstract void takeNotes();        @Override        public  void run() {              takeNotes();        }; } 运行结果(程序并没有结束而是一直卡住无法往下运行)

    线程的生命周期

    新建态 :Thread t = new Thread(),线程对象被创建,此时还没有使用start()方法,启动线程 就绪状态 :t.start()后进入就绪状态,但具体什么时候运行取决于CPU的调度 运行态:线程占用CPU运行 阻塞态:线程在运行时,使用了Thread.sleep()或者进行I/O操作等不在占用CPU,则由运行态进入阻塞态,待事件结束再进入就绪态 t.stop()线程结束,进入终止态(消亡状态)

    了解更多相关经验的知识,可以关注云图智联

    免费学习视频欢迎关注云图智联:https://e.yuntuzhilian.com/

    Processed: 0.014, SQL: 9