博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 基础篇:第十九章:多线程
阅读量:2121 次
发布时间:2019-04-30

本文共 12154 字,大约阅读时间需要 40 分钟。

主要内容:

  1. 多线程基础和原理
  2. Thread类和Runnable接口
  3. Thread常见方法
  4. 线程同步

补充Properties两个问题:

1、是Hashtable的子类,可以直接使用父类的put(K key,V value)方法,但是一般不推荐

使用。因为我们在使用Properties的对象时,属性列表中每个键及其对应值都是一个字符串。一般尽量使用(String key, String value)

2、Properties本身内部是无序的,但是在执行store()方法时,方法内部使用了枚举,最终生成的文件是有序的。

一、多线程基础和原理:

1.1 多线程的引入:

线程:是程序执行的一条路径,一个进程可以包含多个线程。进程是一个动态的概念,线程是一个静态的概念。

多线程并发执行可以提高程序的效率,可以同时完成多项工作。

多线程的应用场景:QQ同时与多人视频或共享桌面、服务器同时处理多个客户端的请求、使用迅雷开启多个线程一起下载......

1.2 多线程并行和并发:

多线程并发:多个任务都要请求运行,而处理器只能接受一个任务,就把这些任务分时段轮流进行,由于时间非常短暂,使人感觉是多个任务同时运行。

多线程并行:就是两个(或以上)的任务同时运行,在某一个进程运行过程中,有其他的进程也在同时进行。(前提是需要多个CPU)

1.3 JVM的运行:

Java程序运行原理:Java命令会开启JVM,启动JVM进程,相当于启动了一个应用程序。该进程会启动一个“主线程”,然后主线程去调用某个类中main方法。

JVM本身是不是多线程?

public class Demo01_Thread {

    /*

     * 判断JVM是不是多线程?——JVM是多线程的。

     */

    public static void main(String[] args) {  // main方法本身是由主线程调用的。

       for (int i = 0; i < 1000000; i++) {  // 开启了自定义的一个线程

           new Demo();

       }

       for (int j = 0; j < 10; j++) {

           System.out.println("我是主线程的执行代码...");

       }

    }

}

class Demo {   // Object类finalize()

    @Override

    public void finalize() {

       System.out.println("垃圾被成功地清除了!!!!!");

    }

}

1.4 多线程的实现方法(Thread):

public class Demo02_Thread {

    /**

     * 使用Thread类实现多线程

     */

    public static void main(String[] args) {

       // 4.创建一个Thread的子类对象(线程)

       MyThread thread = new MyThread();

       // 5.开启线程

       thread.start();

       for (int i = 0; i < 1000; i++) {  // 主线程的代码

           System.out.println("Nature & Peace");

       }

    }

}

class MyThread extends Thread {  // 1.继承Thread

    @Override

    // 注解

    public void run() {   // 2.重写run方法

       for (int i = 0; i < 1000; i++) {  // 3.将要执行的代码,写在run中

           System.out.println("相信自己");

       }

    }

}

1.5 多线程的实现方式(Runnable):

public class Demo03_Thread {

    /**

     * 继承Runnable接口,来实现多线程

     */

    public static void main(String[] args) {

       // 4.创建MyRunnable的对象

       MyRunnable myRunnable = new MyRunnable();

       // 5.开启线程

       Thread thread = new Thread(myRunnable);

       thread.start();

       // 主线程的代码

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

           System.out.println("Nature & Peace.");

       }

    }

}

class MyRunnable implements Runnable {  // 1.定义一个类实现Runnable接口

    @Override

    public void run() {  // 2.实现接口中的run()方法

       for (int i = 0; i < 1000; i++) {  // 3.将要执行的代码写在run方法中

           System.out.println("相信自己");

       }

    }

}

1.6 两种实现方式的区别:

1、继承Thread类:由于子类重写Thread类的run()方法,当我们执行start()方法时,直接找子类run()方法。编译的时候,调用Runnable接口中的run()方法声明。

2、实现Runnable接口:Thread构造函数中传入Runnable接口对应的一个实例对象的引用,成员变量获得该引用,start()方法调用run()方法时内部判断成员变量Runnable的引用是否为空,不空编译时看Runnable中的run()方法声明,运行时执行的是实现类中的run()方法。

1.7 匿名内部类实现多线程:

public class Demo04_Thread {

    /**

     * 使用内部类实现多线程

     */

    public static void main(String[] args) {

       new Thread() {   // 1.继承Thread类(使用的是匿名类)

           @Override

           public void run() { // 2.重写run()方法

              for (int i = 0; i < 1000; i++) {  // 3.将要执行的代码,写在run中

                  System.out.println("相信自己");

              }

           }

       }.start();   // 4.开启线程

       // 使用内部类+Runnable接口实现多线程

       new Thread(new Runnable() {   // 1.使用匿名类写一个Runnable接口的实例对象,传给Thread的构造方法

                  @Override

                  public void run() {  // 2.实现run()方法

                     for (int i = 0; i < 1000; i++) {   // 3.要执行的代码放在run()方法中

                         System.out.println("Nature & Peace.");

                     }

                  }

              }).start();   // 4.开启线程

       // for (int i = 0; i < 1000; i++) { // 主线程的代码

       // System.out.println("Nature & Peace.");

       // }

    }

}

  • Thread类的常用方法:

2.1 获取名字和设置名字:

public class Demo01_Name {

    /**

     * 线程的名称:getName()/setName(String name)

     */

    public static void main(String[] args) {

       // demo01();

       Thread thread1 = new Thread() {  // 1.继承Thread(Thread类的对象thread1指向Thread类的一个子类匿名对象的引用)

           @Override

           public void run() {   // 2.重写run()方法

              for (int i = 0; i < 1000; i++) {   // 3.run方法中的代码

                  // 此处的this代表当前的这Thread子类,使用getName()方法获取线程名称

                  System.out.println(this.getName()

                         + "----------Do trust me!");

              }

           }

       };

       Thread thread2 = new Thread() {  // 1.继承Thread

           @Override

           public void run() {   // 2.重写run()方法

              for (int i = 0; i < 1000; i++) {   // 3.run方法中的代码

                  System.out.println(this.getName() + "..........请相信我!");

              }

           }

       };

       thread1.setName("铁拐李");  // 4.设置线程的名称

       thread2.setName("猪悟能");

       thread1.start();   // 5.启动线程

       thread2.start();

    }

    /**

     *

     */

    private static void demo01() {

       new Thread("铁拐李") {  // 1.继承Thread(调用构造方法设置线程的名称)

           @Override

           public void run() {   // 2.重写run()方法

              for (int i = 0; i < 1000; i++) {   // 3.run方法中的代码

                  // 此处的this代表当前的这Thread子类,使用getName()方法获取线程名称

                  System.out.println(this.getName()

                         + "----------Do trust me!");

              }

           }

       }.start();  // 4.启动线程

       new Thread("二师兄") {  // 1.继承Thread

           @Override

           public void run() {   // 2.重写run()方法

              for (int i = 0; i < 1000; i++) {   // 3.run方法中的代码

                  System.out.println(this.getName() + "..........请相信我!");

              }

           }

       }.start();  // 4.启动线程

    }

}

2.2 获取当前线程的对象:

public class Demo02_CurrentThread {

    /**

     * public static Thread currentThread():返回对当前正在执行的线程对象的引用。

     */

    public static void main(String[] args) {

       new Thread() {

           public void run() {

              System.out.println(getName() + ".......Just do it!");

           }

       }.start();

       new Thread(new Runnable() {

           @Override

           public void run() {

              System.out.println(Thread.currentThread().getName()

                     + "--------干就完了。");

           }

       }).start();

       // Thread.currentThread().setName("我是主线程");

       System.out.println(Thread.currentThread().getName());

    }

}

2.3 休眠线程:

public static void sleep(long millis) throws InterruptedException:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

public class Demo03_Sleep {

    /**

     * sleep(long millis)方法,线程休眠

     */

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

       // demo01();

       new Thread() {  // 继承了Thread类

           public void run() {

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

                  try {

                     Thread.sleep(500);

                  } catch (InterruptedException e) {

                     e.printStackTrace();

                  }

                  System.out.println(getName() + "--------Just do it.");

              }

           }

       }.start();

       new Thread() {

           public void run() {

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

                  try {

                     Thread.sleep(450);

                  } catch (InterruptedException e) {

                     e.printStackTrace();

                  }

                  System.out.println(getName() + ".........干就完了!");

              }

           }

       }.start();

    }

    /**

     * sleep方法实现倒计时

     */

    private static void demo01() throws InterruptedException {

       for (int i = 20; i > 0; i--) {

           Thread.sleep(1000);  // 1000ms=1s

           System.out.println("敌军将在" + i + "秒内到达战场!");

       }

    }

}

2.4 守护线程:

public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。

public class Demo04_Daemon {

    /**

     * 守护线程setDaemon(boolean on)

     */

    public static void main(String[] args) {

       Thread thread1 = new Thread() {

           public void run() {

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

                  System.out.println(getName() + "........你愁啥?" + i);

              }

           }

       };

       Thread thread2 = new Thread() {

           public void run() {

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

                  System.out.println(getName() + "........瞅你咋滴?" + i);

              }

           }

       };

       thread2.setDaemon(true);

       // 设置为守护线程,被守护的是thread1

       // 当thread1线程完全执行结束之后,thread2应该立即结束;实际上thread2的结束会有非常小的时间延迟

       thread1.start();

       thread2.start();

    }

}

2.5 加入线程:

public final void join(long millis) throws InterruptedException:等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。

public class Demo05_Join {

    /**

     * 线程加入:join(long millis)

     */

    public static void main(String[] args) {

       // 匿名内部类要访问方法的局部变量,该变量必须使用final修饰。

       final Thread thread1 = new Thread() {

           public void run() {

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

                  System.out.println(getName() + "--------Just do IT.");

              }

           }

       };

       Thread thread2 = new Thread() {

           public void run() {

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

                  if (i == 5) {

                     try {

                         thread1.join(1);

                     } catch (InterruptedException e) {

                         e.printStackTrace();

                     }

                  }

                  System.out.println(getName() + ".........学好IT!");

              }

           }

       };

       thread1.start();

       thread2.start();

    }

}

2.6 礼让线程:

public static void yield():暂停当前正在执行的线程对象,并执行其他线程。

public class Demo06_Yield {

    /**

     * yield()方法让出CPU资源,交给其他线程

     */

    public static void main(String[] args) {

       new MyThread().start();

       new MyThread().start();

    }

}

class MyThread extends Thread {

    @Override

    public void run() {

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

           if (i % 10 == 0) {

              Thread.yield();

              try {

                  Thread.sleep(1);

              } catch (InterruptedException e) {

                  e.printStackTrace();

              }

           }

           System.out.println(getName() + "-------" + i);

       }

    }

}

2.7 线程优先级:

public final static int MIN_PRIORITY = 1;  最低优先级

    public final static int NORM_PRIORITY = 5;  默认优先级

    public final static int MAX_PRIORITY = 10;   最高优先级

public class Demo07_Priority {

    /**

     * 线程的优先级

     */

    public static void main(String[] args) {

       Thread thread1 = new Thread() {

           public void run() {

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

                  System.out.println(getName() + ".......二师兄,你好!");

              }

           }

       };

       Thread thread2 = new Thread() {

           public void run() {

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

                  System.out.println(getName() + ".......沙师弟!");

              }

           }

       };

       thread1.setPriority(Thread.MAX_PRIORITY);  // 设置最高优先级(10)

       thread2.setPriority(Thread.MIN_PRIORITY);  // 设置最低优先级(1)

       thread1.start();

       thread2.start();

    }

}

三、线程同步:

3.1 同步代码块:

同步代码块,根据锁机制,可以是任意对象,必须是同一个对象。

不能使用匿名对象,因为匿名对象不是同一个对象

锁对象可以直接使用this关键字,前提是这两个方法必须是同一个类的成员方法(非static)。

public class Demo01_Synchronized {

    /**

     * 同步代码块

     */

    public static void main(String[] args) {

       final Printer printer = new Printer();

       new Thread() {

           public void run() {

              while (true) {

                  printer.print01();

              }

           }

       }.start();

       new Thread() {

           public void run() {

              while (true) {

                  printer.print02();

              }

           }

       }.start();

       System.out.println("aaa");

    }

}

class Printer {

    Demo demo = new Demo();

    public void print01() {

       // 同步代码块,根据锁机制,可以是任意对象,必须是同一个对象。

       // 不能使用匿名对象,因为匿名对象不是同一个对象

       // 锁对象可以直接使用this关键字,前提是这两个方法必须是同一个类的成员方法(非static)

       // synchronized (demo) {

       synchronized (this) {

           System.out.print("相");

           System.out.print("信");

           System.out.print("自");

           System.out.print("已");

           System.out.print("\r\n");

       }

    }

    public void print02() {

       // synchronized (demo) {

       synchronized (this) {

           System.out.print("坚");

           System.out.print("持");

           System.out.print("努");

           System.out.print("力");

           System.out.print("吧");

           System.out.print("\r\n");

       }

    }

}

class Demo {

}

3.2 同步方法:

非静态的同步方法的锁对象是this

静态的同步方法的锁对象是该类的字节码对象

public class Demo02_SynchronizedFunc {

    /**

     * 同步代码块

     */

    public static void main(String[] args) {

       final Print prt = new Print();

       new Thread() {

           public void run() {

              while (true) {

                  prt.print01();

              }

           }

       }.start();

       new Thread() {

           public void run() {

              while (true) {

                  prt.print03();

              }

           }

       }.start();

       System.out.println("aaa");

    }

}

class Print {

    // 非静态的同步方法的锁对象就是this

    // 静态的同步方法的锁对象是字节码文件(Print.class)

    public static synchronized void print01() {

       System.out.print("相");

       System.out.print("信");

       System.out.print("自");

       System.out.print("已");

       System.out.print("\r\n");

    }

    // public static synchronized void print02() {

    // System.out.print("坚");

    // System.out.print("持");

    // System.out.print("努");

    // System.out.print("力");

    // System.out.print("吧");

    // System.out.print("\r\n");

    // }

    // 如果print01()方法被定义为非静态的同步方法,那么此时应该使用this作为同步锁

    // 如果print01()方法被定义为静态的同步方法,那么此时应该使用该类的字节码(Print.class)作为同步锁

    public void print03() {

       synchronized (Print.class) {

           System.out.print("坚");

           System.out.print("持");

           System.out.print("努");

           System.out.print("力");

           System.out.print("吧");

           System.out.print("\r\n");

       }

    }

}

3.3 线程安全问题:

1、通过集成Thread类实现:

public class Demo03_SafetyTicket {

    /**

     * 线程安全问题——火车票

     */

    public static void main(String[] args) {

       new TicketThread().start();

       new TicketThread().start();

       new TicketThread().start();

       new TicketThread().start();

    }

}

class TicketThread extends Thread {

    // 多个线程中使用同一个变量时,该变量必须使用static

    private static int number = 100;

    @Override

    public void run() {

       while (true) {

           synchronized (TicketThread.class) {

              if (number == 0) {

                  break;

              }

              try {

                  Thread.sleep(10);  // 线程0睡、线程1睡、线程2睡、线程3睡

              } catch (InterruptedException e) {

                  e.printStackTrace();

              }

              System.out

                     .println(getName() + "------这是第" + number-- + "号火车票!");

           }

       }

    }

}

2、通过实现Runnable接口来实现

public class Demo04_SafetyTicket {

    /**

     * @param args

     */

    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 number = 100;

    @Override

    public void run() {

       while (true) {

           synchronized (this) {

              if (number == 0) {

                  break;

              }

              try {

                  Thread.sleep(50);

              } catch (InterruptedException e) {

                  e.printStackTrace();

              }

              System.out.println(Thread.currentThread().getName()

                     + "......这是第" + number-- + "号火车票!");

           }

       }

    }

}

3.4 多线程的死锁:

public class Demo05_DeadLock {

    private static String str1 = "左筷子";

    private static String str2 = "右筷子";

    // 做开发时,千万不要使用锁嵌套

    public static void main(String[] args) {

       new Thread() {

           public void run() {

              while (true) {

                  synchronized (str1) {

                     System.out.println(getName() + "......获取到【" + str1

                            + "】,等待【" + str2 + "】");

                     synchronized (str2) {

                         System.out.println(getName() + "------只要拿到了【"

                                + str2 + "】就开吃!!!!!");

                     }

                  }

              }

           }

       }.start();

       new Thread() {  // 第二个线程

           public void run() {

              while (true) {

                  synchronized (str2) {

                     System.out.println(getName() + "......获取到【" + str2

                            + "】,等待【" + str1 + "】");

                     synchronized (str1) {

                         System.out.println(getName() + "------只要拿到了【"

                                + str1 + "】就开吃!!!!!");

                     }

                  }

              }

           }

       }.start();

    }

}

转载地址:http://ouyrf.baihongyu.com/

你可能感兴趣的文章
CMake 入门实战
查看>>
绑定CPU逻辑核心的利器——taskset
查看>>
Linux下perf性能测试火焰图只显示函数地址不显示函数名的问题
查看>>
c结构体、c++结构体和c++类的区别以及错误纠正
查看>>
Linux下查看根目录各文件内存占用情况
查看>>
A星算法详解(个人认为最详细,最通俗易懂的一个版本)
查看>>
利用栈实现DFS
查看>>
逆序对的数量(递归+归并思想)
查看>>
数的范围(二分查找上下界)
查看>>
算法导论阅读顺序
查看>>
Windows程序设计:直线绘制
查看>>
linux之CentOS下文件解压方式
查看>>
Django字段的创建并连接MYSQL
查看>>
div标签布局的使用
查看>>
HTML中表格的使用
查看>>
(模板 重要)Tarjan算法解决LCA问题(PAT 1151 LCA in a Binary Tree)
查看>>
(PAT 1154) Vertex Coloring (图的广度优先遍历)
查看>>
(PAT 1115) Counting Nodes in a BST (二叉查找树-统计指定层元素个数)
查看>>
(PAT 1143) Lowest Common Ancestor (二叉查找树的LCA)
查看>>
(PAT 1061) Dating (字符串处理)
查看>>