0
点赞
收藏
分享

微信扫一扫

【java高级】一: 多线程

萨科潘 2022-02-24 阅读 59

目录

01、基本概念:程序、进程、线程

1.1 进程与线程

 ​

02 线程的创建和使用

2.1 Thread 类

2.2 API中创建线程的两种方式

2.2.1 方式一 继承Thread类

2.2.2 练习

2.2.3 Thread类中常用方法

2.2.4 线程的调度

 2.2.5 实现Runnable 接口

2.2.6 两种方式的对比

 03 线程的生命周期

04 线程的同步

4.1 理解线程的安全问题

4.2 Java中同步机制解决线程安全问题

4.2.1 同步代码块(synchronized()

4.2.2 同步代码块解决实现Runnable接口中的安全问题

4.2.3 同步代码块解决继承Thread类中的安全问题

4.2.4 同步方法

 4.2.5 使用同步方法解决实现Runnable接口的线程安全问题

4.2.6 使用同步方法解决继承Thread类的线程安全问题

 4.2.7 关于同步方法的总结:

4.3 线程安全的单例模式之懒汉式

05 线程的死锁问题

 06解决线程安全问题方法三:lock锁

 07 线程的通信

 7.1 sleep()和wait()的异同

 7.2 经典例题

 08 JDK 5.0 新增的创建线程的方式

8.1 实现Callable接口

8.2 线程池


01、基本概念:程序、进程、线程

  • 程序(program):为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
  • 进程(process):程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
    • 如:运行中的QQ,运行中的MP3播放器程序是静态的,进程是动态的
  • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
  • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径
    • 若一个进程同一时间并行执行多个线程,就是支持多线程的。
    • 线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。
    • 一个进程中的多个线程共享相同的内存单元/内存地址空间—》它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患

1.1 进程与线程

 

单核CPU,其实是一种假的多线程因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费)。但是因为CPU时间单元特别短,因此感觉不出来
如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

  • 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
  • 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
  • 程序需要同时执行两个或多个任务。
  • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  • 需要一些后台运行的程序时。

02 线程的创建和使用

Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。(java支持多线程)

  • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
  • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

2.1 Thread 类

Thread():创建新的Thread对象
Thread(String threadname):创建线程并指定线程实例名
Thread(Runnabletarget):指定创建线程的目标对象,它实现了Runnable接口中的run方法
Thread(Runnable target, String name):创建新的Thread对象
 

2.2 API中创建线程的两种方式

2.2.1 方式一 继承Thread类

  • 定义子类继承 Thread 类。
  • 子类中重写 Thread 类中的 run 方法。
  • 创建 Thread 子类对象,即创建了线程对象。
  • 调用线程对象 start 方法:启动线程,调用 run 方法 。
class MyThread extends Thread{
    //重写Thread类的run(),需要这个线程做的事
    @Override
    public void run() {
        for(int i = 1;i < 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + i);  // 获取线程
            }
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        //3.创建Thread类的子对象
        MyThread t1 = new MyThread();

        //4.通过此对象调用start():①启动当前线程 ②JVM调用当前线程的run()
        t1.start();

        //如下操作仍在main线程中执行的
        for(int i = 1;i < 100;i++){
            if(i % 2 == 0){
                System.out.println(i + "***main()***");
            }
        }
    }
}

2.2.2 练习

/**
 * 练习:创建两个分线程,其中一个遍历100以内的偶数,另一个遍历100以内的奇数

创建两个Thread子类,分别重写run()
 */
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        m1.start();

        MyThread2 m2 = new MyThread2();
        m2.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);
            }
        }
    }
}

class MyThread2 extends Thread{
    @Override
    public void run() {
        for(int i = 0;i < 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
/**
 * 练习:创建两个分线程,其中一个遍历100以内的偶数,另一个遍历100以内的奇数
 */
public class ThreadDemo {
    public static void main(String[] args) {

        //创建Thread类的匿名子类的方式
        new Thread(){
            @Override
            public void run() {
                for(int i = 0;i < 100;i++){
                    if(i % 2 == 0){
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                for(int i = 0;i < 100;i++){
                    if(i % 2 != 0){
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                    }
                }
            }
        }.start();
    }
}

2.2.3 Thread类中常用方法

class HelloThread extends Thread{
    @Override
    public void run() {
        for(int i = 0;i < 100; i++){

            try {
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
//            if(i % 20 == 0){
//                yield();  //释放当前线程的执行权,但不一定会释放成功
//            }
        }
    }

    public HelloThread(String name){
        super(name);
    }
}

public class ThreadModeTest {
    public static void main(String[] args) {
        HelloThread h1 = new HelloThread("Thread : 1");

//        h1.setName("线程一");

        h1.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 {
                    h1.join();  // 线程主线程进入阻塞状态,直到h1执行完毕
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println(h1.isAlive());
    }
}

2.2.4 线程的调度

/**
 * - 线程的优先级等级
 *   - MAX_PRIORITY:10
 *   - MIN _PRIORITY:1
 *   - NORM_PRIORITY:5 --->默认优先级
 * - 涉及的方法
 *   - getPriority() :返回线程优先值
 *   - setPriority(intnewPriority) :改变线程的优先级
 *
 *   说明:高优先级的线程要抢占低优先级线程cpu的执行权。
 *       但是只是从概率上讲,高优先级的线程高概率的情况下被执行。
 *       并不意味着只有当高优先级的线程执行完以后,低优先级的线程才会被执行。
 */

class HelloThread extends Thread {
    @Override
    public void run() {
        for (int j = 0; j < 100; j++) {

//            try {
//                sleep(10);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }

            if (j % 2 == 0) {
                System.out.println(getName() + ":" + getPriority() + ":" + j);
            }
        }
    }
    public HelloThread(String name){
        super(name);
    }
}

public class ThreadModeTest {
    public static void main(String[] args) {
        HelloThread h2 = new HelloThread("Thread : 1");
        h2.start();

        //设置分线程的优先级
        h2.setPriority(Thread.MAX_PRIORITY);

        //给主线程命名
        Thread.currentThread().setName("主线程");
        Thread.currentThread().setPriority((Thread.MIN_PRIORITY));

        for(int j = 0;j < 100; j++){
            if(j % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + j);
            }

//            if(j == 20){
//                try {
//                    h2.join();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
        }

        System.out.println(h2.isAlive());
    }
}
/**
 * 例子:创建三个c窗口卖票,总票数为100张
 *
 * 存在线程的安全问题,待解决。
 */
class Windows extends Thread{

    private static int ticket = 100;  //这里不是静态变量的话,会卖出三百张票,每个线程都会设置100

    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(getName() + ":卖票,票号为: " + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

public class WindowsTest {
    public static void main(String[] args) {
        Windows t1 = new Windows();
        Windows t2 = new Windows();
        Windows t3 = new Windows();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

 2.2.5 实现Runnable 接口


//1.创建一个实现了Runnable接口得类
class MThread implements Runnable{

    //2.实现类去实现Runnable中的抽象方法:run()
    @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) {
        //3.创建实现类的对象
        MThread m1 = new MThread();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(m1);
        //5.通过Thread类的对象调用start():①启动线程 ②调用当前线程的run() --> 调用了Runnable类型的target的run()
        t1.start();

        //再启动一个线程,遍历100以内的偶数
        Thread t2 = new Thread(m1);
        t2.setName("线程2");
        t2.start();
    }
}

2.2.6 两种方式的对比

 03 线程的生命周期

04 线程的同步

4.1 理解线程的安全问题

提出问题:

​ 多个线程执行的不确定性引起执行结果的不稳定

​ 多个线程对账本的共享,会造成操作的不完整性,会破坏数据。

 

class MThread implements Runnable{
    private int tickets = 100;
    @Override
    public void run() {
        while(true){
            // 进入阻塞,会提高出现错票的几率
       
            if(tickets > 0){
                try {
                    sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                System.out.println(Thread.currentThread().getName() + " 卖票:票号为" + tickets);
                tickets--;
            }else{
                break;
            }
        }
    }
}


public class ThreadTest1 {
    public static void main (String[] args){
        // 实现类对象
        MThread t1 = new MThread();
        // Thread对象,构造器参数为runnable对象
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t1);
        Thread thread3 = new Thread(t1);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        // 调用start
        thread1.start();
        thread2.start();
        thread3.start();

    }


}

 当某个线程操作车票尚未完成时,其他线程就参与进来了,会出现错票(0,-1 等)

如何解决: 加锁(类似于排队上厕所)

当一个线程a操作ticketes 时,其他线程不能参与进来,直到线程a操作完成,其他线程才能参与进来,即使a陷入阻塞

4.2 Java中同步机制解决线程安全问题

4.2.1 同步代码块(synchronized()

//方式一:同步代码块
  synchronized(同步监视器){
      //需要被同步的代码(操作共享数据的代码)

   }
  • 共享数据: 多个线程同时操作的数据\变量 : tickets
  • 同步监视器:俗称:锁,任何一个类的对象都可以充当锁
    • 要求:多个线程必须公用同一把锁
  • 同步的方式,解决了线程的安全问题。---好处 *
  • 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。---局限性

4.2.2 同步代码块解决实现Runnable接口中的安全问题

import static java.lang.Thread.sleep;

class MThread implements Runnable{
    private int tickets = 100;
    Object obj = new Object();  // 共用的锁,保证唯一性
    @Override
    public void run() {
        // Object obj = new Object(); 每个线程进来都会new新的锁,不行

        while(true){
            synchronized (obj){  // 这里可以考虑用this实现锁,因为只有一个对象
                if(tickets > 0){
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 卖票:票号为" + tickets);
                    tickets--;
                }else{
                    break;
                }
            }

        }
    }
}


public class ThreadTest1 {
    public static void main (String[] args){
        // 实现类对象
        MThread t1 = new MThread();
        // Thread对象,构造器参数为runnable对象
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t1);
        Thread thread3 = new Thread(t1);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        // 调用start
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

4.2.3 同步代码块解决继承Thread类中的安全问题

class window extends Thread{
    private static  int tickets = 100;
   private static Object obj = new Object(); // 这里必须是static
    @Override
    public void run() {
        while (true){
            synchronized(obj){
                if (tickets > 0){
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + " :卖票, 票号为" + tickets);
                    tickets--;
                }else{
                    break;
                }
            }

        }
    }
}
public class WindowTest {
    public static void main(String[] args){
        window w1 = new window();
        window w2 = new window();
        window w3 = new window();
        w1.setName("窗口一");
        w2.setName("窗口二");
        w2.setName("窗口三");
        w1.start();
        w2.start();
        w3.start();
    }
}

4.2.4 同步方法

 4.2.5 使用同步方法解决实现Runnable接口的线程安全问题

class Windows3 implements Runnable {

    private int ticket = 100;

    @Override
    public void run() {
        while (true) {
            show();
        }
    }

    public synchronized void show() { //同步监视器:this
//        synchronized (this){
            if (ticket > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为: " + ticket);
                ticket--;
            }
//        }
    }
}

public class WindowsTest3 {
    public static void main(String[] args) {
        Windows3 w3 = new Windows3();

        Thread t1 = new Thread(w3);
        Thread t2 = new Thread(w3);
        Thread t3 = new Thread(w3);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

4.2.6 使用同步方法解决继承Thread类的线程安全问题

/**
 * 使用同步方法处理继承Thread类的方式中的线程安全问题
 */
class Windows4 extends Thread {

    private static int ticket = 100;

    @Override
    public void run() {

        while (true) {

            show();
        }

    }
    private static synchronized void show(){//同步监视器:Window4.class,当前类
        //private synchronized void show(){ //同步监视器:t1,t2,t3。此种解决方式是错误的
        if (ticket > 0) {

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
            ticket--;
        }
    }
}


public class WindowsTest4 {
    public static void main(String[] args) {
        Windows4 t1 = new Windows4();
        Windows4 t2 = new Windows4();
        Windows4 t3 = new Windows4();


        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

    }
}

 4.2.7 关于同步方法的总结:

 *  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
 *  2. 非静态的同步方法,同步监视器是:this
 *     静态的同步方法,同步监视器是:当前类本身(继承类,一般采用静态数据和静态方法)

4.3 线程安全的单例模式之懒汉式

/**
 * 使用同步机制将单例模式中的懒汉式改写为线程安全的
 */
public class BankTest {
}

class Bank{
        // 私有化构造器

    private Bank(){}
        // 内部构造类的实例,且必须静态化

    private static Bank instance = null;
   // 提供公共方法,返回实例

    public static Bank getInstance(){
        //方式一:效率稍差
        //快捷键:Alt+Shift+Z
//        synchronized (Bank.class) {
//            if(instance == null){
//                instance = new Bank();
//            }
//            return instance;
//        }

        //方式二:效率较高
        if(instance == null) {
            synchronized (Bank.class) {
                if (instance == null) {  // 双检锁
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}

05 线程的死锁问题

import static java.lang.Thread.sleep;

public class ThreadTEST {
    /*
演示线程死锁问题
 */
        public static void main(String[] args) {

            StringBuffer s1 = new StringBuffer();
            StringBuffer s2 = new StringBuffer();

            new Thread() {
                @Override
                public void run() {
                    synchronized (s1) {
                        s1.append('a');
                        s2.append("1");
                        // 举例进行死锁,无输出,无法执行
                        try {
                            sleep(100);
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }

                        synchronized (s2) {
                            s1.append("b");
                            s2.append("2");

                            System.out.println(s1);
                            System.out.println(s2);
                        }


                    }
                }

            }.start();
                // 实现Runnabel接口的匿名对象
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (s2) {
                        s1.append('c');
                        s2.append("3");
                        try {
                            sleep(100);
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }

                        synchronized (s1) {
                            s1.append("d");
                            s2.append("4");

                            System.out.println(s1);
                            System.out.println(s2);
                        }

                    }

                }
            }).start();

        }
}

/*无sleep输出
ab
12
abcd
1234,也可能Thread2先进行,输出 12,ab,cdab,3412
 */

 06解决线程安全问题方法三:lock锁

import java.util.concurrent.locks.ReentrantLock;

/*
解决线程安全方式三: lock锁
在线程中声明一个属性,造一个ReentrantLoc对象
synchronized和lock的异同:
同: 二者都可以解决线程安全问题
不同:
1:lock()需要手动启动同步和结束同步(unlock),而synchronized在执行完相应同步代码后自动释放同同步监视器
2.lock()只有代码块锁,sychronized有方法块锁和方法所
3. lock锁有根本更好的性能
 */
class Window implements Runnable{
    private int tickets = 100;
    // 1.在线程中声明一个属性,实例化一个ReentrantLoc对象
    private ReentrantLock lock = new ReentrantLock();

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

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

       Thread t1 = new Thread(w) ;
       Thread t2 = new Thread(w) ;
       Thread t3 = new Thread(w) ;

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

    }


}
/**
 * 银行有一个账户。
 * 有两个储户分别向同一个账户存3000元,每次存1000,存3次。
 * 每次存完打印账户余额。
 *
 * 分析:
 *      1.是否是多线程问题?是,两个储户线程
 *      2.是否有共享数据?有,账户(或账户余额)
 *      3.是否有线程安全问题?有
 *      4.需要考虑如何解决线程安全问题?同步机制:有三种方式。
 */
class Account{
    private double balance;

    public Account(double balance){
        this.balance = balance;
    }

    //存钱,同步方法解决继承多线程要慎用
    public synchronized void deposit(double amt){
        if(amt > 0){

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            balance += amt;
            System.out.println(Thread.currentThread().getName() + ":" + "存钱成功,当前余额:" + balance);
        }
    }
}

class Customer extends Thread{

    private Account acct;
    public Customer(Account acct){
        this.acct = acct;
    }

    @Override
    public void run() {

        for(int i = 0;i < 3;i++){
            acct.deposit(1000);
        }
    }
}

public class AccountTest {
    public static void main(String[] args) {
        Account acct = new Account(0);
        Customer c1 = new Customer(acct);
        Customer c2 = new Customer(acct);

        c1.setName("甲");
        c2.setName("乙");

        c1.start();
        c2.start();
    }
}

 07 线程的通信

涉及到的三个方法:
 * wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器o1.wait();
 * notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
 * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
 *
 * 说明:
 *      1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
 *      2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常
 *      3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。

/*
使用两个线程打印 1 100 。线程 1, 线程 2 交替打印
 */
class Number extends Thread{
    private  static int number = 1;
    public static Object o1 = new Object();
    @Override
    public void run() {
        while(true){
            synchronized (o1){
                o1.notify();//唤醒当前线程,如果不加o1,会报错!

                if(number <= 100){
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "打印:" + number);
                    number++;
                    try {

                        // 使得掉用当前wait的线程进入阻塞状态
                        o1.wait();  //释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }
            }

        }
    }
}

public class ComunicationTest {
    public static void main(String[] args){
        Number number1 = new Number();
        Number number2 = new Number();

        number1.setName("线程1");
        number2.setName("线程2");

        number1.start();
        number2.start();


    }

}

 7.1 sleep()和wait()的异同

 7.2 经典例题

 

/*
1.是否是多线程问题: 是,生产者和消费者线程
2.是否有共享数据:是,店员(clerk),产品
3.如何解决:同步的三种方法
4.通信:wait();notify();是,如果超过20,令线程进入阻塞
 */
class Clerk{
    private int productNum = 0;

    //生产产品
    public synchronized void productProduct() {
        if(productNum < 20){
            productNum++;
            System.out.println(Thread.currentThread().getName() +
                    "开始生产第 " + productNum + "个产品");
            // 使用wait之后就要添加notify,如果不添加,消费者会一直处于阻塞状态
            notify();

        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //消费产品
    public synchronized void consumeProduct() {
        if(productNum > 0){
            System.out.println(Thread.currentThread().getName() + "开始消费第 "
                    + productNum + "个产品");
            productNum--;
            notify();
        }else{
            //自始至终只造了一个clerk对象,所以锁是共用的
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
class Producer extends Thread{ //生产者
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始生产");
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.productProduct();
        }

    }
}

class Customer extends Thread {//消费者
    private Clerk clerk;

    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始消费");
        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }

    }
}

public class ProductTest {
    public static void main(String[] args){
        Clerk clerk = new Clerk();

       Producer p1 =  new Producer(clerk);
       p1.setName("生产者");

       Customer c1 = new Customer(clerk);
       c1.setName("消费者");

       p1.start();
       c1.start();


    }

}

 08 JDK 5.0 新增的创建线程的方式

8.1 实现Callable接口

 

/*
方式三:实现Callable接口的方式
1.创建实现Callable接口的实现类
2.实现call方法,将此线程需要执行的操作声明在call方法中,同时call需要返回值
3.创建callanle实现类对象
4. 将此实现类对象作为参数传递到FutherTask构造器中,创FutherTask对象
5,将futherTask的对象作为参数传递到Thread类构造器中,并调用Start方法
 */

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1.创建实现Callable接口的实现类
class NumberThread implements Callable {

    @Override
    //2.实现call方法,将此线程需要执行的操作声明在call方法中,同时call需要返回值
    public Object call() throws Exception {
        int sum = 0;
        for(int i = 1; i <= 100; i++){
            if (i % 2 ==0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;// 这里进行了装箱
    }
}
public class CallableTest {
    public static void main(String[] args){
        // 3.创建callanle实现类对象
        NumberThread numberThread = new NumberThread();
        //4. 将此实现类对象作为参数传递到FutherTask构造器中,创FutherTask对象
        FutureTask futureTask = new FutureTask(numberThread);//Callable的实现类

       // 5,将futherTask的对象作为参数传递到Thread类构造器中,并调用Start方法
        new Thread(futureTask).start();

        try {
            // get()返回值 即为重写的返回值
            Object sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }


}

8.2 线程池

 

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

/**
 * 创建多线程的方式四:使用线程池
 *
 * 好处:
 *      1.提高响应速度(减少了创建新线程的时间)
 *      2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 *      3.便于线程管理
 *          corePoolSize:核心池的大小
 *          maximumPoolSize:最大线程数
 *          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);
            }
        }
    }
}

class NumberThread1 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 service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //强转
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();

        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());  //适合适用于Runable
        service.execute(new NumberThread1());  //适合适用于Runable

//        service.submit(Callable callable);   //适合适用于Callable

        //3.关闭连接池
        service.shutdown();
    }
}
举报

相关推荐

0 条评论