• Java入门笔记 05-多线程


    介绍:Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程。本章主要内容为:多线程的创建、启动、控制以及同步操作,并介绍JDK 5新增的线程创建方式。

    一、线程的创建与使用

      1. 继承Thread类创建线程

    • 建立一个继承Thread的子类
    • 重写Thread类的run()--把所执行的操作写在该方法中
    • 创建一个子类对象
    • 通过该对象调用start()方法
     1 //1.1. 建立一个继承Thread的子类
     2 class MyThread1 extends Thread{
     3     @Override
     4     //1.2. 重写Thread类的run()
     5     public void run() {
     6         for(int i=0;i<100;i++){
     7             if(i%2==0){
     8                 System.out.println(getName() + ":" + i);//Thread.currentThread().getName()
     9             }
    10         }
    11     }
    12 }
    13 
    14 //1.3. 创建一个子类对象
    15 MyThread1 t1 = new MyThread1();
    16 t1.setName("线程1");
    17 
    18 //1.4. 通过该对象调用start()方法
    19 t1.start();//注意,一个线程对象不能同时两次start,需要重新创建一个对象来start

      2. 实现Runnable接口创建线程

    • 建立一个Runnable接口的实现类
    • 实现Runnable中的抽象方法run()
    • 创建实现类对象
    • 将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
    • 通过Thread类的对象调用start()方法
     1 //2.1 建立一个Runnable接口的实现类
     2 class MyThread2 implements Runnable{
     3     //2.2. 实现Runnable中的抽象方法run()
     4     @Override
     5     public void run() {
     6         for(int i=0;i<100;i++){
     7             if(i%2==0){
     8                 System.out.println(Thread.currentThread().getName() + ":" + i);
     9             }
    10         }
    11     }
    12 }
    13 
    14 //2.3 2.4 创建实现类对象,将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
    15 Thread t2 = new Thread(new MyThread2());
    16 t2.setName("线程2");
    17 
    18 //2.5. 通过Thread类的对象调用start()方法
    19 t2.start();

      开发中优先选择后者:既没有类的单继承性的限制,而且实现的方式更适合来处理多个线程有共享数据的情况。

      3. 实现Callable接口创建线程

    • 创建一个Callable接口的实现类
    • 实现call方法,将此线程需要执行的操作写在方法体中
    • 创建Callable接口实现类的对象
    • 将上面的实现类对象作为参数创建FutureTask的对象
    • 将上面的FutureTask的对象作为参数创建Thread的对象
    • 利用Thread的对象调用start方法
    • 通过 FutureTask的对象.get() 方法获取call方法的返回值
     1 //3.1 创建一个Callable接口的实现类
     2 class NumThread implements Callable{
     3     @Override
     4     //3.2 实现call方法,将此线程需要执行的操作写在方法体中
     5     public Object call() throws Exception {
     6         int sum = 0;
     7         for(int i=1;i<=100;i++){
     8             if(i%2==0){
     9                 System.out.println(i);
    10                 sum += i;
    11             }
    12         }
    13         return sum;
    14     }
    15 }
    16 
    17 //3.3 创建Callable接口实现类的对象
    18 NumThread num = new NumThread();
    19 //3.4 将上面的实现类对象作为参数创建FutureTask的对象
    20 FutureTask futureTask = new FutureTask(num);
    21 //3.5 将上面的FutureTask的对象作为参数创建Thread的对象
    22 Thread t = new Thread(futureTask);
    23 t.setName("线程3");
    24 //3.6 利用Thread的对象调用start方法
    25 t.start();
    26 
    27 try {
    28     //3.7 通过 FutureTask的对象.get() 方法获取call方法的返回值
    29     Object sum = futureTask.get();//get方法的返回值即为NumThread.call的返回值
    30     System.out.println("共计:" + sum);
    31 } catch (InterruptedException e) {
    32     e.printStackTrace();
    33 } catch (ExecutionException e) {
    34     e.printStackTrace();
    35 }

      相较于Runnable接口更强大、可以抛出异常、支持泛型,但是需要借助FutureTask类

      4. 使用线程池创建线程

    • 创建指定线程数量的线程池
    • 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
    • 关闭连接池
     1 class MyThread2 implements Runnable{
     2     @Override
     3     public void run() {
     4         for(int i=0;i<100;i++){
     5             if(i%2==0){
     6                 System.out.println(Thread.currentThread().getName() + ":" + i);
     7             }
     8         }
     9     }
    10 }
    11 
    12 //4.1 创建指定线程数量的线程池
    13 ExecutorService service = Executors.newFixedThreadPool(10);//创建确定个数的线程池
    14 // 4.2 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
    15 service.execute(new MyThread2());//参数只能是Runnable
    16 service.execute(new MyThread3());//参数只能是Runnable
    17 //4.3 关闭连接池
    18 service.shutdown();

       5. Thread类中常见的方法

    • void start(): 启动线程,并执行对象的run()方法
    • run(): 线程在被调度时执行的操作
    • String getName(): 返回线程的名称
    • void setName(String name):设置该线程名称
    • static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
    • static void yield(): 线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法
    • join() : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,使得低优先级的线程也可以获得执行
    • static void sleep(long millis):(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。(抛出InterruptedException异常)
    • stop(): 强制线程生命期结束,不推荐使用
    • boolean isAlive(): 返回boolean,判断线程是否还活着
    //给线程命名--方式一:通过setName()
    t1.setName("线程1");                             //设置该线程名称
    
    //给线程命名--方式二:通过构造器
    ThreadMethodTest2 t2 = new ThreadMethodTest2("*线程2");

      6. 线程优先级

    • MAX_PRIORITY:10
    • MIN _PRIORITY:1
    • NORM_PRIORITY:5

      涉及的方法

    • getPriority() :返回线程优先值
    • setPriority(int newPriority) :改变线程的优先级

      说明

    • 线程创建时继承父线程的优先级
    • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

    二、线程的同步

      1. 同步代码块

    synchronized (同步监视器){
        // 需要被同步的代码;
    }

      同步监视器:俗称锁。任何一个类的对象都可以充当锁,要求多个线程必须共用同一个锁

    •  Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
    • 继承Thread类的方式不能用this,可以使用 类.class 来充当

      2. 同步方法:synchronized放在方法声明中,表示整个方法为同步方法。同步方法仍然涉及同步监视器,但无需显示说明,例如:

    public synchronized void show (String name){
        ....
    }

      3. lock锁

    //1. 实例化ReentrantLock
    try{
        //2. 调用lock
        //需要被同步的代码
    }finally{
        //3. 调用unlock
    }

      4. 案例

     1 /**
     2  * 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
     3  * 存在线程的安全问题,采用 同步代码块方式 解决
     4  * synchronized(同步监视器){
     5  *      //需要被同步的代码
     6  * }
     7  *      > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
     8  *                  要求多个线程必须共用同一个锁。
     9  *      > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
    10  *      > 继承Thread类的方式不能用this,可以使用 类.class 来充当
    11  * @author shkstart
    12  * @create 2019-02-13 下午 4:47
    13  */
    14 class Window1 implements Runnable{
    15 
    16     private int ticket = 100;   //共享数据
    17     private Object obj = new Object();
    18 
    19     @Override
    20     public void run() {
    21         while(true){
    22             synchronized (obj){     //synchronized(this)
    23                 if(ticket > 0){
    24                     System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
    25                     ticket--;
    26                 }else{
    27                     break;
    28                 }
    29             }
    30         }
    31     }
    32 }
    33 
    34 
    35 public class TicketSafeBR {
    36     public static void main(String[] args) {
    37         Window1 w = new Window1();
    38 
    39         Thread t1 = new Thread(w);
    40         Thread t2 = new Thread(w);
    41         Thread t3 = new Thread(w);
    42 
    43         t1.setName("窗口1");
    44         t2.setName("窗口2");
    45         t3.setName("窗口3");
    46 
    47         t1.start();
    48         t2.start();
    49         t3.start();
    50     }
    51 
    52 }
    Runnable-同步代码块方式
     1 /**
     2  * 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
     3  * 存在线程的安全问题,采用 同步方法方式 解决
     4  *
     5  * > 同步方法仍然涉及同步监视器,但无需显示说明
     6  * > 非静态同步方法的同步监视器是:this
     7  * > 静态同步方法的同步监视器是:类.class
     8  *
     9  *
    10  * @author shkstart
    11  * @create 2019-02-13 下午 4:47
    12  */
    13 class Window2 implements Runnable{
    14 
    15     private int ticket = 100;
    16 
    17     @Override
    18     public void run() {
    19         while(true){
    20             show();
    21         }
    22     }
    23 
    24     private synchronized void show(){   //同步监视器为this
    25         if(ticket > 0){
    26             try {
    27                 Thread.sleep(100);
    28             } catch (InterruptedException e) {
    29                 e.printStackTrace();
    30             }
    31             System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
    32             ticket--;
    33         }
    34     }
    35 }
    36 
    37 
    38 public class TicketSafeMR {
    39     public static void main(String[] args) {
    40         Window2 w = new Window2();
    41 
    42         Thread t1 = new Thread(w);
    43         Thread t2 = new Thread(w);
    44         Thread t3 = new Thread(w);
    45 
    46         t1.setName("窗口1");
    47         t2.setName("窗口2");
    48         t3.setName("窗口3");
    49 
    50         t1.start();
    51         t2.start();
    52         t3.start();
    53     }
    54 
    55 }
    Runnable-同步方法方式
     1 /**
     2  *
     3  * 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
     4  *
     5  * 存在线程的安全问题,采用 同步代码块方式 解决
     6  * synchronized(同步监视器){
     7  *      //需要被同步的代码
     8  * }
     9  *      > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
    10  *                  要求多个线程必须共用同一个锁。
    11  *      > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
    12  *      > 继承Thread类的方式不能用this,可以使用 类.class 来充当(类.class只会加载一次)(反射)
    13  * @author shkstart
    14  * @create 2019-02-13 下午 4:20
    15  */
    16 class Window extends Thread{
    17     private static int ticket = 100;
    18     //要求多个线程必须共用同一个锁,因此使用了static,
    19     private static Object obj = new Object();//一定要注意,这里与Runnable接口的方式有所不同
    20     @Override
    21     public void run() {
    22 
    23         while(true){
    24             synchronized (obj){  //synchronized(Window.class)
    25                 if(ticket > 0){
    26                     try {
    27                         Thread.sleep(100);
    28                     } catch (InterruptedException e) {
    29                         e.printStackTrace();
    30                     }
    31                     System.out.println(getName() + ":卖票,票号为:" + ticket);
    32                     ticket--;
    33                 }else{
    34                     break;
    35                 }
    36             }
    37         }
    38     }
    39 }
    40 
    41 
    42 public class TicketSafeBT {
    43     public static void main(String[] args) {
    44         Window t1 = new Window();
    45         Window t2 = new Window();
    46         Window t3 = new Window();
    47 
    48 
    49         t1.setName("窗口1");
    50         t2.setName("窗口2");
    51         t3.setName("窗口3");
    52 
    53         t1.start();
    54         t2.start();
    55         t3.start();
    56 
    57     }
    58 }
    Thread-同步代码块方式
     1 /**
     2  *
     3  * 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
     4  *
     5  * 存在线程的安全问题,使用 同步方法方式 来解决
     6  *
     7  * > 同步方法仍然涉及同步监视器,但无需显示说明
     8  * > 非静态同步方法的同步监视器是:this
     9  * > 静态同步方法的同步监视器是:类.class
    10  *
    11  *
    12  * @author shkstart
    13  * @create 2019-02-13 下午 4:20
    14  */
    15 class Window3 extends Thread{
    16 
    17 
    18     private static int ticket = 100;
    19     @Override
    20     public void run() {
    21         while(true){
    22             show();
    23         }
    24     }
    25 
    26     private static synchronized void show(){   //同步监视器:Window3.class
    27     //private static synchronized void show(){   //此种方法错误,同步监视器:t1、t2、t3(不唯一)
    28         if(ticket > 0){
    29             try {
    30                 Thread.sleep(100);
    31             } catch (InterruptedException e) {
    32                 e.printStackTrace();
    33             }
    34             System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
    35             ticket--;
    36         }
    37     }
    38 }
    39 
    40 
    41 public class TicketSafeMT {
    42     public static void main(String[] args) {
    43         Window3 t1 = new Window3();
    44         Window3 t2 = new Window3();
    45         Window3 t3 = new Window3();
    46 
    47 
    48         t1.setName("窗口1");
    49         t2.setName("窗口2");
    50         t3.setName("窗口3");
    51 
    52         t1.start();
    53         t2.start();
    54         t3.start();
    55 
    56     }
    57 }
    Thread-同步方法方式
     1 public class LockTest {
     2     public static void main(String[] args) {
     3         Window4 w = new Window4();
     4 
     5         Thread t1 = new Thread(w);
     6         Thread t2 = new Thread(w);
     7         Thread t3 = new Thread(w);
     8 
     9         t1.setName("窗口1");
    10         t2.setName("窗口2");
    11         t3.setName("窗口3");
    12 
    13         t1.start();
    14         t2.start();
    15         t3.start();
    16 
    17     }
    18 
    19 }
    20 
    21 
    22 class Window4 implements Runnable{
    23 
    24     private int ticket = 100;
    25 
    26     //1. 实例化ReentrantLock
    27     private ReentrantLock lock = new ReentrantLock();
    28 
    29     @Override
    30     public void run() {
    31         while (true){
    32             try{
    33                 //2. 调用lock
    34                 lock.lock();
    35                 if(ticket>0){
    36                     try {
    37                         Thread.sleep(100);
    38                     } catch (InterruptedException e) {
    39                         e.printStackTrace();
    40                     }
    41                     System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
    42                     ticket--;
    43                 }else
    44                     break;
    45             }finally {
    46                 //3. 调用unlock
    47                 lock.unlock();
    48             }
    49         }
    50     }
    51 }
    Lock锁方式

    三、线程的通信

    • wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
    • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
    • notifyAll ():唤醒正在排队等待资源的所有线程结束等待.
     1 /**
     2  * 线程的通信:
     3  *      两个线程交替打印1-100:
     4  *          > wait()
     5  *          > notify()/notifyAll()
     6  *          > 注意:wait和notify的调用者必须是同一个对象
     7  *          > wait和notify必须使用在同步代码块或者同步方法中
     8  *
     9  * @author marc
    10  * @creat 2020-02-07 上午9:22
    11  */
    12 public class MsgTest {
    13     public static void main(String[] args) {
    14         Number n1 = new Number();
    15 
    16         Thread t1 = new Thread(n1);
    17         Thread t2 = new Thread(n1);
    18 
    19         t1.setName("线程1");
    20         t2.setName("线程2");
    21 
    22         t1.start();
    23         t2.start();
    24 
    25     }
    26 }
    27 
    28 class Number implements Runnable{
    29     private int i = 1;
    30     @Override
    31     public void run() {
    32         while (true){
    33 
    34             synchronized (this) {
    35 
    36                 notify();         //this.notify()
    37 
    38                 if(i<=100){
    39                     System.out.println(Thread.currentThread().getName() + ":" + i);
    40                     i++;
    41 
    42                     try {
    43                         wait();    //this.wait()
    44                     } catch (InterruptedException e) {
    45                         e.printStackTrace();
    46                     }
    47 
    48                 }else
    49                     break;
    50             }
    51 
    52         }
    53     }
    54 }
  • 相关阅读:
    Maven学习总结(八)——使用Maven构建多模块项目
    Maven学习总结(七)——eclipse中使用Maven创建Web项目
    Maven学习总结(六)——Maven与Eclipse整合
    Maven学习总结(五)——聚合与继承
    BBS的登陆——发帖——回帖
    bugfree,CDbConnection 无法开启数据库连线: SQLSTATE[HY000] [2003] Can't connect to MySQL server on '192.168.0.99' (4)
    Mac 中配置Apache
    Mac里安装配置Jdk
    启动mongodb遇到的错:warning: 32-bit servers don't have journaling enabled by deflity
    分享组2015.7.31
  • 原文地址:https://www.cnblogs.com/dailymatters/p/12292990.html
Copyright © 2020-2023  润新知