乐趣区

Java线程的创建与使用

一、继承于 Thread 类

1. 创建一个继承于 Thread 类的子类
2. 重写 Thread 类的 run(), 将此线程的执行操作声明在 run() 中
3. 创建 Thread 类的子类的对象
4. 通过此对象调用 start()


class MyThread extends Thread {
    @Override
    public void run() {for(int i = 0; i < 100; i++) {if(i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadTest {public static void main(String[] args) {MyThread t1 = new MyThread();
        t1.start();}
}

二、Thread 的常用方法

/**
 * 1.start(): 启动当前线程:调用当前线程的 run()
 * 2.run(): 通常需要重写 Thread 类的此方法,将创建的线程要执行的操作声明在此方法中
 * 3.currentThread(): 静态方法,返回当前代码执行的线程
 * 4.getName():获取当前线程的名字
 * 5.setName():设置当前线程的名字
 * 6.yield(): 释放当前 cpu 的执行权, 但还是有可能被分配到
 * 7.join(): 在线程 a 中调用线程 b 的 join(); 此时线程 a 就进入阻塞状态,直到线程 b 完全执行完以后,线程 a 才结束阻塞状态。* 8.stop():强制终止线程
 * 9.sleep(long millitime): 让当前线程睡眠指定的 millitime 毫秒。在指定的 millitime 毫秒时间内,当前线程是阻塞状态
 * 10.isAlive(): 判断当前线程是否活着
 */
class HelloThread extends Thread {
    @Override
    public void run() {for (int i = 0; i < 100; i++) {if (i % 2 == 0) {
                try {sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadMethodTest {public static void main(String[] args) {HelloThread hello = new HelloThread();
        hello.setName("线程 1");
        hello.start();

        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":" + i);
            }

            if (i == 20) {
                try {hello.join();
                } catch (InterruptedException e) {e.printStackTrace();
                }
            }
        }
    }
}

三、Thread 的调度

调度方法:
1. 同优先级线程组成先进先出队列,使用时间片策略
2. 对高优先级,使用优先调度的抢占式策略
线程优先级:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是高优先级线程之后才被调用

四、实现 Runnable 接口

/**
 * 1. 创建一个实现了 Runnable 接口的类
 * 2. 实现类去实现 Runnable 中的抽象方法:run()
 * 3. 创建实现类的对象
 * 4, 将此对象作为参数传递到 Thread 类的构造器中,创建 Thread 类的对象
 * 5. 通过 Thread 类的对象调用 start()
 */
class twThread implements Runnable {
    @Override
    public void run() {for (int i = 0; i < 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadTest1 {public static void main(String[] args) {twThread tw = new twThread();
        Thread thread1 = new Thread(tw);
        thread1.start();}
}

五、线程的生命周期

线程的状态:
新建:当一个 Thread 类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。
就绪:处于新建状态的线程被 start()后,将进入线程的队列等待 CPU 时间片,此时它已具备了运行条件,只是没有分配到 CPU 资源。
运行:当就绪的线程被调度并获得 CPU 资源时,便进入运行状态,run()方法定义了线程的操作和功能。
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时终止自己的执行,进入阻塞状态。
死亡:线程完成了它的全部工作或线程被提前强制性地终止或出现异常导致结束。

六、线程安全

package com.study.thread;

/**
 *  1. 出现重票,线程安全问题
 *  2. 原因:当某个线程操作车票过程中,尚未操作完成时,其他线程进来,也操作车票
 *  3. 解决:当一个线程 a 操作 ticket 时,其他线程不能进来,直到 a 操作完 ticket 时,线程才可以开始操作,ticket,这种情况即使线程 a 出现了阻塞,也不能被改变
 *  4,在 java 中通过同步机制,解决线程安全问题
 *  方式一:同步代码块
 *
 *  synchronized(同步监视器) {
 *     // 需要被同步的代码
 *  }
 * 说明:1. 操作共享数据的代码,即为需要被同步的代码, 不能包含代码多了,也不少
 *     2. 共享数据:多个线程共同操作的变量,比如 ticket
 *     3. 同步监视器:俗称锁,任何一个类的对象,都可以充当锁
 *          要求:多个线程必须要用同一把锁
 *          补充:在实现 Runnable 接口创建多线程方式中,我们可以考虑使用 this 充当同步监视器
 *          继承 Thread 类创建多线程,慎用 this 当同步监视器,考虑用当前类充当同步监视器
 *  方式二:同步方法
 *  如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
 *
 *  5. 同步的方式:解决了线程安全问题
 *   操作同步代码时只能有一个线程参与,其他线程等待,相当于单线程过程,效率低
 */
class Windows implements Runnable {
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {synchronized(obj) {while(ticket > 0) {
                try {Thread.sleep(100);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
                ticket--;
            }
        }
    }
}
public class WindowTest {public static void main(String[] args) {Windows w1 = new Windows();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.start();
        t2.start();
        t3.start();}
}

JDK5.0 新语法

class Window implements Runnable {
    private int ticket = 100;
    // 实例化 ReentrantLock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {while (true) {
            try {
                // 调用 lock
                lock.lock();
                if (ticket > 0) {
                    try {Thread.sleep(100);
                    } catch (InterruptedException e) {e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket);
                    ticket--;
                } else {break;}
            }finally {
                // 解锁
                lock.unlock();}


        }
    }
}

public class LockTest {public static void main(String[] args) {Window window = new Window();

        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
        t1.setName("窗口 1");
        t2.setName("窗口 2");
        t3.setName("窗口 3");

        t1.start();
        t2.start();
        t3.start();}
}

七、线程通信

package com.study.channel;
/**
 * 线程通信:两个线程交替打印 1 -100 数字
 * 涉及到 3 个方法
 * wait()一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
 * notify(): 一旦执行此方法,就会唤醒被 wait()的一个线程,如果有多个线程被 wait,就唤醒优先级高的
 * notifyall()一旦执行此方法,就会唤醒所有 wait 的线程
 *
 * 说明:*      这三个方法必须放到同步代码块或同步方法中
 *      这三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现异常
 * sleep() 和 wait()的异同
 * 相同点:一旦执行方法,都可以使得当前线程进入阻塞状态
 * 不同点:*      两个方法声明的位置不同:Thread 类中声明 sleep()方法,Object 类中声明 wait()方法
 *      sleep()可在任何场景下调用,wait()必须在同步代码块中调用
 *      如果两个方法都在同步代码块中调用,sleep()不会释放同步监视器,wait()会释放同步监视器(锁)*
 */
class Number implements Runnable {
    private int number = 100;

    @Override
    public void run() {while (true) {synchronized (this) {notifyAll();
                if (number >= 1) {
                    try {Thread.sleep(10);
                    } catch (InterruptedException e) {e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number--;
                    try {
                        // 使得调用如下 wait 如下方法的线程进入阻塞状态
                        wait();} catch (InterruptedException e) {e.printStackTrace();
                    }
                } else {break;}
            }
        }
    }
}

public class CommuncationTest {public static void main(String[] args) {Number num = new Number();
        Thread t1 = new Thread(num);
        Thread t2 = new Thread(num);
        Thread t3 = new Thread(num);
        t1.setName("线程 1");
        t2.setName("线程 2");
        t3.setName("线程 3");

        t1.start();
        t2.start();
        t3.start();}
}

八、实现 Callable 创建多线程

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 比实现 Runnable 强大
 * 1. 支持返回值
 * 2. 支持抛异常
 * 3. 泛型
 */

// 1. 创建一个实现 Callable 的实现类
class NumThread implements Callable {
    // 实现 call 方法,将线程要做的操作生命在 call 方法中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {if (i % 2 == 0) {sum += i;}
        }
        return sum;
    }

}

public class ThreadNew {public static void main(String[] args) {
        // 3. 创建 callable 实现类的对象
        NumThread num = new NumThread();
        // 4. 将此 Callbale 实现类的对象传递到 FutureTask 构造器中,创建 FutureTask 对象
        FutureTask futureTask = new FutureTask(num);
        // 5. 将 futureTask 传入 Thread 构造器
        new Thread(futureTask).start();
        try {
            // 6. 获取到相应返回值
            Object sum = futureTask.get();
            System.out.println("返回值:" + sum);
        } catch (InterruptedException e) {e.printStackTrace();
        } catch (ExecutionException e) {e.printStackTrace();
        }
    }
}

九、线程池创建多线程

package com.study.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 使用线程池创建线程
 * 好处:* 1. 提升响应速度,* 2. 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
 * 3。便于点成管理
 *      corePloolSize  核心池的大小
 *      maxinumPloolSize  最大线程数
 *      keepAliveTime 线程没有任务时,最多保存多长时间终止
 */
class NumberThread implements Runnable {
    @Override
    public void run() {for (int i = 0; i <= 100;i++) {if(i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadPool {public static void main(String[] args) {
        // 1. 提供指定线程数量的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //2. 执行指定的线程操作,需要提供实现 Runnable 接口或者 Callable 接口实现类的对象
        executorService.execute(new NumberThread()); // 适用于 Runnable
//        executorService.submit(Callable callable) // 适用于 Callable
        //3. 关闭连接池
        executorService.shutdown();}
}
退出移动版