【Java_16】线程

2021-02-04 10:06:53 浏览数 (1)

一、一些概念

1. 并行与并发
代码语言:javascript复制
* 并行是指多个事件在同一时刻发生
* 并发是指多个事务在同一个时间段内发生
2. 进程与线程
代码语言:javascript复制
* 我们运行一个软件就是一个进程
* 一个进程可以包含多个线程

二、线程

1. 创建线程方式一
代码语言:javascript复制
① 格式
    * 一个类继承 Thread 类并实现 run() ,调用 start() 启动线程。
② 常用方法
    * getName() 
        获取当前线程名称
    * currentThread()
        获取当前线程
    * sleep(long n)
        使当前线程睡眠 n 毫秒
③ 示例
    //创建一个类继承 Thread
    public class MyThread extends Thread {
        //重写 run() 方法
        @Override
        public void run() {
            //创建线程任务
            System.out.println("创建了一个线程");
        }
    }

    //测试类
    public class DemoThread {
        public static void main(String[] args) {
            //创建线程对象
            MyThread thread = new MyThread();
            //启动线程
            thread.start();
        
            //使用匿名内部类创建线程并启动
            new Thread() {
                @Override
                public void run() {
                    System.out.println("创建了一个线程");
                }
            }.start();
        }
    }
2. 创建线程方式二
代码语言:javascript复制
① 格式
    * 一个类实现 Runnable 接口并重写 run() 方法,在将实现类对象传到 Thread 中使用 start() 启动线程
② 使用 Runnable 的好处
    * 避免的单继承的局限性
    * 适合多个线程共享一个资源对象
③ 示例
    //创建一个类实现 Runnable 接口并重写 run() 方法
    public class RunnableImpl implements Runnable {
        //重写 run() 方法
        @Override
        public void run() {
            //创建线程任务
            System.out.println("创建了一个线程");
        }
    }

    //测试类
    public class DemoRunnable {
        public static void main(String[] args) {
            //创建 Runnable 接口实现类对象
            RunnableImpl impl = new RunnableImpl();
            //使用 Thread 构造方法创建线程
            Thread thread = new Thread(impl);
            //启动线程
            thread.start();
        
            //使用匿名内部类创建线程
            new Thread(new Runnable() {
                //重写 run() 方法
                @Override
                public void run() {
                    for (int i = 20; i > 0; i--) {
                        System.out.println("创建了一个线程");
                    }
                }
            }).start();
        }
    }

三、线程安全

1. 线程不安全的原因
代码语言:javascript复制
① 多个线程访问同一个资源时
② 某一个线程在操作完资源,但是还未进行记录时,失去了 CPU 执行权
③ 另一个线程获得了 CPU 的执行权,对资源进行操作,这就导致了线程不安全
2. 同步代码块(synchronized)
代码语言:javascript复制
① 格式
    synchronized (锁对象) {
        //同步内容
    }
② 注意
    * 锁对象可以是任意对象,但是多个线程使用的锁对象必须是同一个。
    * 一个线程获取锁对象进入同步代码块时,其他进程就算获取了 CPU 执行权也无法进入同步代码块,
     只有等锁对象释放以后,获取到锁对象后才能访问。
③ 示例
    public class DemoTicket {
        public static void main(String[] args) {
            //使用匿名内部内创建Runnable对象
            Runnable r = new Runnable() {
                //总票数
                int num = 100;

                @Override
                public void run() {
                    //使循环一直开启
                    while (true) {
                        //同步代码块
                        /*
                            锁可以使用 对象名、this还可以使用该类的字节码文件
                        */
                        synchronized (DemoTicket.class) {
                            //判断是否有余票
                            if (num > 0) {
                                try {
                                    //模拟卖票时间
                                    Thread.sleep(0);
                                    //模拟出票
                                    System.out.println(Thread.currentThread().getName()   "正在卖第"   num   "张票");
                                    //票数减1
                                    num--;
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
 
                }
 
            };
 
            //开启线程
            new Thread(r, "1号窗口").start();
            new Thread(r, "2号窗口").start();
            new Thread(r, "3号窗口").start();
 
        }
    }
3. 同步方法
代码语言:javascript复制
① 格式
    修饰符 synchronized 返回值 方法名(参数列表) {
        //方法体
    }
② 示例    
    public class DemoTicket {
        public static void main(String[] args) {
            //使用匿名内部内创建Runnable对象
            Runnable r = new Runnable() {
                //总票数
                int num = 100;

                @Override
                public void run() {
                    //使循环一直开启
                    while (true) {
                        ticket(num);
                    }
 
                }
 
            };
 
            //开启线程
            new Thread(r, "1号窗口").start();
            new Thread(r, "2号窗口").start();
            new Thread(r, "3号窗口").start();
 
        }
    }
    
    public static synchronized void ticket(int num) {
        //判断是否有余票
        if (num > 0) {
            try {
                //模拟卖票时间
                Thread.sleep(0);
                //模拟出票
                System.out.println(Thread.currentThread().getName()   "正在卖第"   num   "张票");
                //票数减1
                num--;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        
    }
4. Lock锁
代码语言:javascript复制
① 格式
    Lock lock = new ReentrantLock();
    lock.lock();
    lock.unlock();
② 示例
    import java.util.concurrent.locks.ReentrantLock;

    public class DemoTicket {
        public static void main(String[] args) {
 
            ReentrantLock lock = new ReentrantLock();
            //使用匿名内部内创建Runnable对象
            Runnable r = new Runnable() {
                //总票数
                int num = 100;
 
                @Override
                public void run() {
 
                    //使循环一直开启
                    while (true) {
                        //同步代码块
                        lock.lock();
                        //判断是否有余票
                        if (num > 0) {
                            try {
                                //模拟卖票时间
                                Thread.sleep(0);
                                //模拟出票
                                System.out.println(Thread.currentThread().getName()   "正在卖第"   num   "张票");
                                //票数减1
                                num--;
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        lock.unlock();
                    }
 
                }
 
            };
 
            //开启线程
            new Thread(r, "1号窗口").start();
            new Thread(r, "2号窗口").start();
            new Thread(r, "3号窗口").start();
 
        }
    }
四、线程状态
1. 七种线程状态
代码语言:javascript复制
① new(新建):线程刚刚被创建,但是并未启动
② Runnable(可运行):线程可以运行但是没有 CPU 执行权
③ Running(运行):线程正在执行过程中
④ Blocked(锁阻塞):线程无法获取到锁对象
⑤ Waiting(无限等待):一个线程在等待另一个线程的唤醒
⑥ Time Waiting(计时等待):线程等待 n 毫秒后自动唤醒
⑦ Teminated(被终止):线程执行结束
2. 等待唤醒机制
代码语言:javascript复制
① 常用方法
    * wait()
        等待另一个线程唤醒
    * wait(long l)
        线程等待 l 毫秒后自动被唤醒
    * notify()
        唤醒在等待中的一个线程,等待最久的有限被唤醒
    * notifyAll()
        唤醒所有等待的线程
② 注意
    * wait() 需要在同步代码块或者同步方法中使用
    * wait() 和 notify() 需要使用同一个锁对象(定义在 Object 中,任何一个类都可以使用)
    * wait() 会释放锁而 sleep() 不会释放锁
③ 示例
    //定义一个标准类
    public class Commodity {
        private String type;
        private Boolean state;
        
        //省略 构造方法 set/get
    }
 
    public class Demo {
        public static void main(String[] args) {
            //创建锁对象
            Object obj = new Object();
 
            //创建商品对象
            Commodity type = new Commodity("烤鸡",false);
 
            //创建厨子线程
            Runnable cook = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        synchronized (obj) {
                            //判断是否有货
                            if (type.getState()) {
                                try {
                                    //有货则等待
                                    obj.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                try {
                                    System.out.println(Thread.currentThread().getName()   "做"   type.getType());
                                    //没有或则生产货
                                    Thread.sleep(0);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                //更行货物状态
                                type.setState(true);
                                //唤醒顾客线程吃
                                obj.notifyAll();
                            }
                        }
                    }
                }
 
            };
 
            //创建顾客线程
            Runnable customer = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        synchronized (obj) {
                            //判断是否有货
                            if (type.getState()) {
                                try {
                                    //有货则吃
                                    System.out.println(Thread.currentThread().getName()   "吃"   type.getType());
                                    Thread.sleep(0);
                                    //更新货物状态
                                    type.setState(false);
 
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                try {
                                    //没货则唤醒厨子线程做
                                    obj.notify();
                                    //等待厨子做
                                    obj.wait();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            };
 
            //启动线程
            new Thread(cook,"厨子").start();
            new Thread(customer,"顾客1:").start();
            new Thread(customer,"顾客2:").start();
        }
    }

五、线程池

1. 线程池思想
代码语言:javascript复制
① 什么是线程池
    * 将创建好的一堆线程扔到一个容器内,想用的时候拿出来,用完了放回去。
② 线程池的好处
    * 降低了资源的消耗,每个线程都重复利用
    * 提高了相应速度,不需要每次都向 CPU 要求开辟通道
    * 便于管理
2. 线程池的使用
代码语言:javascript复制
① 格式
    ExecutorService threadPool = Executors.newFixedThread(int nThreads);
    threadPool.submit(Runnable r);
② 常用方法
    * shutdown()
        不允许在提交新的线程,已提交的线程执行结束后关闭线程池
    * shutdownNow()
        立即停止所有线程
    * submit(Callable<T> task)
        提交一个线程,返回一个处理结果
③ 示例
    public  class Commodity { 
        private String type; 
        private Boolean state;
		
        //省略 构造方法 set/get
    }

    public class Demo {
        public static void main(String[] args) {
            //创建锁对象
            Object obj = new Object();
 
            //创建商品对象
            Commodity type = new Commodity("烤鸡",false);
 
            //创建厨子线程
            Runnable cook = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        synchronized (obj) {
                            //判断是否有货
                            if (type.getState()) {
                                try {
                                    //有货则等待
                                    obj.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                try {
                                    System.out.println(Thread.currentThread().getName()   "做"   type.getType());
                                    //没有或则生产货
                                    Thread.sleep(0);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                //更行货物状态
                                type.setState(true);
                                //唤醒顾客线程吃
                                obj.notifyAll();
                            }
                        }
                    }
                }
 
            };
 
            //创建顾客线程
            Runnable customer = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        synchronized (obj) {
                            //判断是否有货
                            if (type.getState()) {
                                try {
                                    //有货则吃
                                    System.out.println(Thread.currentThread().getName()   "吃"   type.getType());
                                    Thread.sleep(0);
                                    //更新货物状态
                                    type.setState(false);
 
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                try {
                                    //没货则唤醒厨子线程做
                                    obj.notify();
                                    //等待厨子做
                                    obj.wait();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            };
 
            //创建线程池
            ExecutorService threadPool = Executors.newFixedThreadPool(5);
            //提交线程任务
            threadPool.submit(cook);
            threadPool.submit(customer);
            threadPool.submit(customer);
        }
    }

0 人点赞