• java多线程编程


    1. 多线程编程

    2. Thread和Runnable

     java中实现多线程的方式有两种,继承Thread类、实现Runnable接口

    2.1 Thread

    开发人员可以编写一个类继承Thread,并重写run方法,在run方法里面编写线程将要执行的代码。

    创建线程对象后,只需要调用start()方法即可让线程进入就绪队列,等待操作系统调度。

    需要特别注意的是调度具有随机性和随时性。也就是说无法确定下一次调度哪个线程,也无法确定什么时刻进行调度

    public class MyThreadTest {
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.start();//把myThread加入到就绪队列里面
        }
    }
    
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("MyThread执行了");
        }
    }

    2.2 Runnable

    除了继承Thread重写run方法外,在简单的情况下,还可通过实现Runnable接口的方式编写线程执行的代码

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Runnable接口方式实现多线程");
        }
    });

    3.线程安全问题

    一个数据,如一个对象或对象中的某个字段,如果有多个线程可以同时访问它,就可能会出现线程安全问题:数据错乱、程序出错或其他无法预知的问题

    比如线程1要遍历一个list,线程2要把这个list清空,如果这两个线程同时执行就可能会出现线程安全问题

    public class ThreadQuestionTest {
    
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<Integer>();
            for (int i = 0; i < 100000; i++) {
                list.add(i);
            }
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < list.size(); i++) {
                        System.out.println(list.get(i));
                    }
                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    list.clear();
                }
            });
            thread1.start();
            thread2.start();
        }
    }

    输出:

    0
    null
    null
    null
    null
    ...
    

    4. 线程同步

    线程同步控制,即使用某种方式使得一个线程在操作完某个数据前,别的线程无法操作这个数据,从而避免多个线程同时操作一个数据,进而避免线程安全问题

    线程同步控制的方式有同步锁机制、等待/通知机制、信号量机制等,它们应用在不同复杂度的场景下

    4.1同步代码块

    synchronized同步锁机制

    Java中每个对象都有一把锁,同一时刻只能有一个线程持有这把锁。线程可以使用synchronized关键字向系统申请某个对象的锁,得到锁之后,别的线程再申请该锁时,就只能等待。持有锁的线程在这次操作完成后,可以释放锁,以便其他线程可以获得锁

    synchronized有两种形式,synchronized代码块和synchronized方法

    synchronized代码块,又称同步代码块:

    public class SynchronizedBlockTest {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<Integer>();
            for (int i = 0; i < 100000; i++) {
                list.add(i);
            }
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (list) {
                        for (int i = 0; i < list.size(); i++) {
                            System.out.println(list.get(i));
                        }
                    }
                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (list) {
                        list.clear();
                    }
                }
            });
            thread1.start();
            thread2.start();
        }
    }
    View Code

    4.2 同步方法

    public class SynchronizedMethodTest {
    
        public static void main(String[] args) {
            Data data = new Data();
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    data.bianliList();
                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    data.clearList();
                }
            });
            thread1.start();
            thread2.start();
        }
    }
    
    class Data {
        private List<Integer> list;
        public Data() {
            list = new ArrayList<Integer>();
            for (int i = 0; i < 100000; i++) {
                list.add(i);
            }
        }
        public synchronized void bianliList() {
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
        }
        public synchronized void clearList() {
            list.clear();
        }
    }
    View Code

    非静态同步方法申请的锁是类的当前对象的锁,静态同步方法申请的锁是类的Class对象的锁。同步方法执行完后即向系统归还锁

    synchronized代码块和synchronized方法的效果一样,可根据具体场景灵活选用 

    对于简单的需要线程同步控制的应用场景,synchronized基本够用

    但需要注意,所有需要同步的线程必须都申请同一个对象的锁,当申请不同的锁或者有的线程没有使用synchronized时,同步锁机制就会失效

    5. wait/notify 等待/通知机制

    对于稍复杂的情况,比如多个线程需要相互合作有规律的访问共享数据,就可以使用wait/notify机制,即等待/通知机制,也称等待/唤醒机制

    等待/通知机制建立在synchronized同步锁机制的基础上,即在同步代码块(或同步方法)内,如果当前线程执行了lockObject.wait()(lockObject表示提供锁的对象),则当前线程立即暂停执行,并被放入阻塞队列,并向系统归还所持有的锁,并在lockObject上等待,直到别的线程调用lockObject.notify()

    如果有多个线程在同一个对象上等待,notify()方法只会随机通知一个等待的线程,也可以使用notifyAll()方法通知所有等待的线程。被通知的线程获得锁后会进入就绪队列

    public class WaitNotifyTest {
        public static void main(String[] args) {
            Object lockObject = new Object();
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lockObject) {
                        try {
                            System.out.println("线程1即将开始在lockObject上等待");
                            lockObject.wait();
                            System.out.println("线程1收到通知并获得锁,开始继续执行");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lockObject) {
                        System.out.println("线程2将随机通知在lockObject上等待的线程");
                        lockObject.notify();
                    }
                }
            });
            thread2.start();
            thread1.start();
        }
    }
    View Code

    5.1 wait/notify-生产者消费者实例

    一个很典型的生产者消费者例子:现有一个生产者、一个消费者、10个盘子(缓冲区),生产者把生产的产品放入空盘子中,当没有空盘子时就停止生产;消费者消费盘子中的产品,当所有的盘子都是空盘子时就停止消费

    public class ProducerConsumerTest{
        public static void main(String[] args) {
            List<Integer> buffer = new LinkedList<Integer>();
            int maxSize = 10;
    
            Producer producer = new Producer(buffer, maxSize);
            Consumer consumer = new Consumer(buffer);
    
            producer.start();
            consumer.start();
        }
    }
    //模拟生产者
    class Producer extends Thread {
    
        private List<Integer> buffer; //缓冲区,表示多个盘子
        private int maxSize; //表示盘子个数
        public Producer(List<Integer> buffer, int maxSize) {
            this.buffer = buffer;
            this.maxSize = maxSize;
        }
        @Override
        public void run() {
            int id = 0;
            while (true) {
                synchronized (buffer) {
                    if (buffer.size() < maxSize) {//有空盘子则继续生产
                        id++;//表示生产了一个产品
                        buffer.add(id); //表示把产品放入一个空盘子中
                        System.out.println("生产产品" + id + "并通知消费者可以消费了");
                        buffer.notify(); //通知消费者有产品可以消费了
                    } else {
                        //如果没有空盘子则等待
                        System.out.println("没有空盘子了,生产者停止生产产品");
                        try {
                            buffer.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    //模拟消费者
    class Consumer extends Thread {
        private List<Integer> buffer; //缓冲区,表示多个盘子
        public Consumer(List<Integer> buffer) {
            this.buffer = buffer;
        }
        @Override
        public void run() {
            while (true) {
                synchronized (buffer) {
                    if (buffer.size() > 0) { //有不空的盘子
                        int id = buffer.remove(0); //表示消费了一个产品
                        System.out.println("消费产品" + id + "并通知生产者有空盘了");
                        buffer.notify();
                    } else {
                        //全部都是空盘子则等待
                        System.out.println("全部都是空盘子,消费者停止消费产品");
                        try {
                            buffer.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    View Code
  • 相关阅读:
    监控文件变化
    哈希+ LIst + 流文件 应用
    (转)Delphi版木马彩衣一个简单的花指令伪装器
    (转)TThread 详解
    遍历菜单
    哈希 + LIST简单应用(DELPHI)
    WINDOWS API速查
    ASP.NET 3.5的页面指令
    感冒怎么治?
    ASPNET应用程序文件夹
  • 原文地址:https://www.cnblogs.com/renjing/p/thread.html
Copyright © 2020-2023  润新知