Day24Java基础学习(多线程)

Day24

  1. 多线程
    (1) 什么时线程
    线程时程序执行的一条路径,一个进程中可以包含多条线程
    多线程并发执行可以提高程序的效率,可以同时完成多项工作。
    (2) 多线程的应用场景
    (1) 红蜘蛛同时共享屏幕给多个电脑
    (2) 迅雷开启多线程一起下载
    (3) QQ同时和多个人一起视频
    (4) 服务器同时处理多个客户端的请求

  2. 多线程(多线程并行和并发的区别)
    (1) 并行就是两个任务同时运行,就是甲任务进行的时候,乙任务也在进行。(需要多核CPU)
    (2) 并发是指两个任务都请求运行,而处理器只能接受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
    (3) 比如我跟两个网友聊天,左手操作一个电脑跟甲聊,右手操作一个电脑跟乙聊,就叫并行。
    (4) 如果用一台电脑我先给甲发个消息,然后立刻给乙发个消息,然后再跟甲聊,再跟乙聊。这就叫并发。

  3. 多线程(Java程序运行原理和JVM的启动是多线程的吗)
    (1) Java程序运行原理
    Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程”,然后主线程取调用某个类的main方法。
    (2) JVM的启动是多线程的吗?
    JVM启动至少启动了垃圾回收线程和主线程,所以是多线程。

public class Demo1_Thread {
    /*
    证明jvm是多先程的
     */
    public static void main(String[] args) {
        for (int i = 0 ; i < 1000000 ; i ++){  //垃圾线程
            new Demo();
        }
        for(int i = 0; i < 100000 ; i++){
            System.out.println("我是主线程的执行代码");
        }

    }
}
class Demo {
    public void finalize(){
        System.out.println("垃圾被清扫了");
    }
}

  1. 多线程(多线程程序实现的方式1)
    (1) 继承Thread
    定义类继承Thread
    重写run方法
    把新线程要做的事情写再run方法中。
    创建线程对象
    开启新线程,内部会自动执行run方法。
public class Demo2_Thread {
    public static void main(String[] args) {
        MyThread mt = new MyThread();  //4:创建Thread类的子类对象
        mt.start();                     //5:开启线程

        for(int i = 0; i < 1000; i++){
            System.out.println("bbb");
        }

    }
}

class MyThread extends Thread {  //1:继承Thread
    public void run() {          //2:重写run方法
        for (int i = 0;i < 1000; i++){  //3:将要执行的代码放在run方法中
            System.out.println("aaaaaaaaaa");
        }
    }
}

  1. 多线程(多线程程序实现的方式2)
    (1) 实现Runnable
    定义类实现Runnable接口
    实现run方法
    把新线程要做的事写在run方法中
    创建自定义的Runnable的子类对象
    创建Thread对象,传入Runnable
    调用star()开启新线程,内部会自动调用Runnable的run()方法。
public class Demo3_Thread {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable(); //4:创建Runnable的子类对象
       Thread t = new Thread(mr);        //5:将其当作参数传递给Thread的构造函数
       t.start();                          //6:开启线程

        for (int i = 0; i < 10000; i++){
            System.out.println("bbbv");
        }

    }
}
  class MyRunnable implements Runnable { //1:定义一个类实现Runnable

      @Override
      public void run() {  //2:重写run方法
          for(int i = 0; i < 10000; i++){ //3:将要执行的代码写在run方法中
              System.out.println("aaaaaaaa");
          }
      }
  }

  1. 多线程(实现Runnable的原理)
    查看源码:
    (1) 看Thread类的构造函数,传递了Runnable接口的引用
    (2) 通过init()方法找到传递的target给成员变量的target赋值
    (3) 查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法。
  2. 多线程(两种方式的区别)
    (1)查看源码的区别:
    继承Thread: 由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法。
    实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法。
    (2) 继承Thread
    好处是:可以直接使用Thread类中的方法,代码简单
    弊端是:如果已经有了父类,就不能用这种方法
    (3) 实现Runnable接口
    好处是:即使自己的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的。
    弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂。
  3. 多线程(匿名内部类实现线程的两种方式)
    继承Thread类
    实现Runnable接口
public class Demo4_Thread {
    public static void main(String[] args) {
        new Thread(){                             //1:继承Thread类
            public void run(){                   //2:重写run方法
                for(int i = 0; i < 1000; i++) {   //3:将要执行的代码写在run方法中
                    System.out.println("aaaaaaaa");
                }
            }
        }.start();                                  //4:开启线程
        
       
        new Thread( new Runnable(){       //1:将Runabble的子类对象传递给Thread的构造方法
            public void run(){           //2:重写run方法
                for(int i = 0;i < 10000; i++){  //3:将要执行的代码写在run方法中
                    System.out.println("bb");
                }
            }
        }).start();   //4:开启线程
    }
}

  1. 多线程(获取名字和设置名字)
    (1) 获取名字
    通过getName()方法获取线程对象的名字
    (2) 设置名字
    通过构造函数可以传入String类型的名字
    通过setName(String)方法可以设置线程对象的名字
public class Demo1_Name {
    public static void main(String[] args) {
       Thread t1 =  new Thread(){
            public void run(){
                System.out.println(this.getName() + "...aaaaaa");
            }
        };

        Thread t2 = new Thread(){
            public void run(){
                System.out.println(this.getName() + "...bbb");
            }
        };
        t1.setName("1233");
        t2.setName("1122");
        t1.start();
        t2.start();
    }


    private static void demo2() {
        new Thread(){
            public void run(){
                this.setName("张三");  //通过setName设置名字
                System.out.println(this.getName() + "...aaaaaa");
            }
        }.start();

        new Thread(){
            public void run(){
                this.setName("李四");
                System.out.println(this.getName() + "...bbb");
            }
        }.start();
    }

    private static void demo1() {
        new Thread("111"){  //通过构造方法命名
            public void run(){
                System.out.println(this.getName() + "...aaaaaa");
            }
        }.start();

        new Thread("222"){
            public void run(){
                System.out.println(this.getName() + "..bbb");
            }
        }.start();
    }
}

  1. 多线程(获取当前线程的对象)
    Thread.currentThread(),主线程也可以获取
public class Demo2_CurrentThread {
    public static void main(String[] args) {
        new Thread(){
            public void run(){
                System.out.println(getName() + "....aaaa");
            }
        }.start();

       new Thread(new Runnable() {
            public void run() {
                //Thread.currentThread()获取当前正在执行的线程
                System.out.println(Thread.currentThread().getName() + "...b");
            }
        }).start();

       Thread.currentThread().setName("我是主线程");  //更改主线程的名字
        System.out.println(Thread.currentThread().getName()) ;//获取主线程的名称
    }
}

11.	多线程(休眠线程)
Thread.sleep(毫秒,纳秒),控制当前线程休眠若干毫秒1=1000毫秒, 
1=1000*10000*1000纳秒 1000000000

public class Demo3_Sleep {
    /*
    Thread.sleep(毫秒,纳秒),控制当前线程休眠若干毫秒1秒=1000毫秒,
   1秒=1000*10000*1000纳秒 1000000000

     */
    public static void main(String[] args) throws InterruptedException {
       new Thread(){
           public void run(){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               for(int i = 0; i < 100; i++){
                   System.out.println(getName() + "..aaa");
               }
           }
       }.start();

       new Thread(){
            public void run(){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for(int i = 0; i < 100; i++){
                    System.out.println(getName() + "..bbb");
                }
            }
        }.start();
    }

    private static void demo1() throws InterruptedException {
        for(int i = 20; i >= 0 ; i --){
            Thread.sleep(1000);
            System.out.println("倒计时第" + i + "秒");
        }
    }
}

  1. setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都执行结束后,自动退出
public class Demo4_SetDaemon {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run(){
                for(int i = 0; i < 2; i ++){
                    System.out.println(getName() + "....aaaaa");
                }
            }
        };
        Thread t2 = new Thread(){
            public void run(){
                for(int i = 0; i < 50; i ++){
                    System.out.println(getName() + "....bb");
                }
            }
        };

        t2.setDaemon(true);  //当传入true就是意味着设置成守护线程
        t1.start();
        t2.start();
    }

}
  1. 多线程(加入线程)
    join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续
    join(int),可以等待指定的毫秒之后继续
public class Demo5_Join {
   final public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run(){
                for (int i = 0; i < 10; i++){
                    System.out.println(getName() + "...aaaaa");
                }
            }
        };

        Thread t2 = new Thread(){
            public void run(){
                for (int i = 0; i < 10; i++){
                    if(i == 2){
                        try {
                           // t1.join();
                            t1.join(1);//设置插队时间,过了指定时间后,两条线程交替执行

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(getName() + "...bbb");
                }
            }
        };

        t1.start();
        t2.start();
    }
}
  1. 多线程(礼让线程)
    Yield让出cpu
public class Demo6_Yield {
    /*
    礼让线程
     */
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();

    }
}
class MyThread extends Thread {
    public void run(){
        for(int i = 1; i <= 1000; i++){
            if(i % 10 == 0){
                Thread.yield();  //让出cpu
            }
            System.out.println(getName() + "..." + i);
        }
    }
}

  1. 多线程(设置线程的优先级)
    setPriority()设置线程的优先级
public class Demo7_SetPriority {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run(){
                for(int i = 0; i < 1000; i++){
                    System.out.println(getName() + "...aaaaaa");
                }
            }
        };

        Thread t2 = new Thread(){
            public void run(){
                for(int i = 0; i < 1000; i++){
                    System.out.println(getName() + "...dd");
                }
            }
        };

       // t1.setPriority(10);  //设置最大优先级
       // t2.setPriority(5);
        
        t1.setPriority(Thread.MAX_PRIORITY); //设置最大优先级
        t2.setPriority(Thread.MIN_PRIORITY);//设置最小优先级
        
        t1.start();
        t2.start();
    }
}

  1. 多线程(同步代码块)
    (1) 什么情况下需要同步
    当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时需要同步。
    如果两段代码是同步的,那么同时只能执行一段,再一段代码执行没有结束之前,不会执行另外一段代码。
    (2) 同步代码块
    使用synchronized关键字加上一个锁对象来定义一段代码,这就交同步代码块。
    多个同步代码块如果使用相同的锁对象,那么他们就是同步的。
public class Demo1_Synchronized {
    /*
    同步代码块
     */
    public static void main(String[] args) {
        final  Printer p = new Printer();

        new Thread() {
            public void run (){
                while (true){
                    p.print1();
                }
            }
        }.start();

        new Thread() {
            public void run (){
                while (true){
                    p.print2();
                }
            }
        }.start();

    }
}
class Printer {
    Demo d = new Demo();
    public void print1() {

        synchronized (d) {   //同步代码块锁机制,锁对象可以是任意的,但是不可以是匿名的
            System.out.print("黑");
            System.out.print("马");
            System.out.print("程");
            System.out.print("序");
            System.out.print("员");
            System.out.print("\r\n");
        }
    }
    public void print2() {
        synchronized (d) {
            System.out.print("传");
            System.out.print("智");
            System.out.print("播");
            System.out.print("客");
            System.out.print("\r\n");
        }
    }
}
class Demo {

}

  1. 多线程(同步方法)
    使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的
    1:非静态的同步方法的锁对象是什么?
    答:非静态的同步方法锁对象是this
    2:静态的同步方法的锁对象是什么?
    答:静态的同步方法锁对象是该类的字节码对象
public class Demo2_Syn {
    public static void main(String[] args) {
            final com.heima.syn.Printer p = new com.heima.syn.Printer();

            new Thread() {
                public void run (){
                    while (true){
                        p.print1();
                    }
                }
            }.start();

            new Thread() {
                public void run (){
                    while (true){
                        p.print2();
                    }
                }
            }.start();

        }
    }
    class Printer2 {
        Demo d = new Demo();
        //非静态的同步方法的锁对象是什么?
        //非静态的同步方法锁对象是this
        //静态的同步方法的锁对象是什么?
        //静态的同步方法锁对象是该类的字节码对象
        public static synchronized  void print1() {  //同步方法,只需要在方法上加 synchronized 关键字即可
                System.out.print("黑");
                System.out.print("马");
                System.out.print("程");
                System.out.print("序");
                System.out.print("员");
                System.out.print("\r\n");
            }

        public static void print2() {
            synchronized (Printer2.class) {
                System.out.print("传");
                System.out.print("智");
                System.out.print("播");
                System.out.print("客");
                System.out.print("\r\n");
            }
        }
    }

  1. 多线程(线程安全问题)
    多线程并发操作同一数据时,就有可能出现线程安全问题
    使用同步技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作
    练习:铁路售票,一共100张,通过4个窗口售完
public class Demo3_Ticket {
    /*
    练习:铁路售票,一共100张,通过4个窗口售完
     */
    public static void main(String[] args) {
        new Ticket().start();
        new Ticket().start();
        new Ticket().start();
        new Ticket().start();

    }

}

class Ticket extends Thread{
   private  static  int ticket = 100; //定义成静态的,可以让四个窗口共享
  // private static Object obj = new Object(); //如果是成员变量当作锁对象,必须是静态的
    public void run(){
        while (true) {
            //synchronized (obj) {  成员变量当作锁对象
            synchronized (Ticket.class) {
                if (ticket == 0) {
                    break;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName() + "这是第" + ticket-- + "号票");
            }
        }

    }
}

  1. 多线程(火车站卖票额例子用实现Runnable接口)
public class Demo4_Ticket {
    public static void main(String[] args) {
      MyTicket mt = new MyTicket();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();

    }
}
class MyTicket implements Runnable {

  private  int tickets = 100;  //此处不需要静态,因为这种方式只需要创建1次对象
    @Override
    public void run() {

        while (true) {
            //synchronized (obj) {  成员变量当作锁对象
            synchronized (Ticket.class) {
                if (tickets == 0) {
                    break;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "这是第" + tickets-- + "号票");
            }
        }

    }
}
  1. 多线程(死锁)
    多线程同步的时候,如果同步代码嵌套,使用相同锁,就有可能出现死锁
    尽量不要嵌套使用
public class Demo5_Lock {
    private static String s1 = "筷子左";
    private static String s2 = "筷子右";

    public static void main(String[] args) {
        new Thread(){
            public void run(){
                while (true){
                    synchronized (s1){
                        System.out.println(getName()+"获取" + s1 + "等待" + s2);
                        synchronized (s2){
                            System.out.println(getName() +"拿到" + s2 + "开吃");
                        }
                    }
                }
            }
        }.start();

        new Thread(){
            public void run(){
                while (true){
                    synchronized (s2){
                        System.out.println(getName()+"获取" + s2+ "等待" + s1);
                        synchronized (s1){
                            System.out.println(getName() +"拿到" + s1 + "开吃");
                        }
                    }
                }
            }
        }.start();
    }
}

  1. 多线程(以前的线程安全的类回顾)
    (1) 回顾以前的线程安全问题
    看源码:Vector ,StringBuffer,Hashtable,Collections,synchronized(xxx)
    Vector是线程安全的,ArrayList是线程不安全的;
    StringBuffer是线程安全的,StringBuilder是线程不安全的;
    Hashtable是线程安全的,HashMap是线程不安全的;
    Collections.synchronized(xxx)可以将线程不安全的变为安全的;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值