• 并发与多线程


    1、创建线程:

    /*
     * 创建线程:
     * 方法一:(1)创建类Thread1继承Thread类,重写run()方法
     * (2)在main线程中创建Thread1对象,并用start
     * */
    public class Thread1 extends Thread{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println("i is :"+i+" "+Thread.currentThread().getName());
            }
        }
    }
    
    public class ThreadTest {
        public static void main(String[] args) throws Exception {
            //创建线程第一种方法:
            for(int i=0;i<5;i++){
                Thread1 thread1=new Thread1();
                thread1.start();
            }
        }
    }
    /*第二种方法:(1)创建Thread2实现Runnable接口,并实现run()方法
                  (2)在主线程main中创建线程,创建Thread对象Thread thread=new Thread(new Thread2()); 调用启动方法start()
    * */
    public class Thread2 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<5;i++){
                System.out.println("i is :"+" "+Thread.currentThread().getName());
            }
        }
    }
    
    //创建线程第二种方法:
            for (int i=0;i<5;i++){
                Thread thread=new Thread(new Thread2());
                thread.start();
            }
            //创建线程第三种方法:创建Runnable内部类
            Thread thread = new Thread(
                    new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(Thread.currentThread().getName());
                        }
                    }
            );
            thread.start();

    2、实现同步

    public class ThreadNum extends Thread {
        private static int num = 50;
    
        //没有使用同步
        @Override
        public void run() {
            while (num > 0) {
                try{
                    sleep(10l);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("num is " + num + " " + Thread.currentThread().getName());
                num--;
            }
        }
    }

    结果:

    num is 50 Thread-1
    .......
    num is 1 Thread-4
    num is 0 Thread-1
    num is -1 Thread-3
    num is -2 Thread-2
    num is -3 Thread-0

    由于没有实现同步,出现了num为负数的情况。

    /*     * 发现出现负数的情况,说明两个以上线程进入了while(num>0)中,所以要实现同步。
         * 方法1:使用同步代码块
         * 语法:synchronized(同步锁){
         *          //需要同步操作的代码
         *       }
         *       同步锁又叫同步监听对象、同步监听器、互斥锁;
         *       任何时候只有一个线程能拿到同步锁;
         *       同步锁可以是相对于线程不变化的对象,一般情况下把当前并发访问的共同资源作为同步监听对象。
         *
         * 方法2:使用同步方法
         *
         * 方法3:使用锁机制
         * */
    
    
    //方法1:使用同步代码块
        @Override
        public void run() {
            //这里使用当前对象的字节码文件作为同步锁
            for (int i = 0; i < 50; i++) {
                synchronized (this.getClass()) {
                    if (num > 0) {
                        try {
                            Thread.sleep(10l);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("num is " + num + " " + Thread.currentThread().getName());
                        num--;
                    }
                }
            }
        }
    
        //方法2:使用同步方法
        @Override
        public void run() {
            for(int i=0;i<50;i++){
                numDown();
            }
        }
    
        private synchronized void numDown() {
            while (num > 0) {
                try {
                    Thread.sleep(10l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("num is " + num + " " + Thread.currentThread().getName());
                --num;
            }
        }
    /**
     * 使用锁的方式实现同步
     */
    public class NumRunnable implements Runnable{
        private static int num=50;
    
        //创建一个锁
        Lock lock=new ReentrantLock();
        @Override
        public void run(){
            for(int i=0;i<50;i++){
                // 获取锁
                lock.lock();
                try{
                    if(num>0){
                        Thread.sleep(10l);
                        System.out.println("num is "+num+","+Thread.currentThread().getName());
                        num--;
                    }
                    Thread.sleep(10l);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    }

    3、生产者和消费者

    public class Person {
        private String name;
        private int age;
    
        private boolean isEmpty = true;//实例变量(单例模式下是非线程安全的),资源对象是否为空,为空则为true,表示需要生产。如果为false,则不需要生产
    
        public synchronized void push(String name, int age) {
            try {
                while (!isEmpty) {//如果不是空的(消费者还未消费)
                    this.wait();
                }
                //-----生产数据开始----
                this.name = name;
                Thread.sleep(10l);
                this.age = age;
                //-----生产数据结束----
                isEmpty = false;//表示不是空的,需要消费者消费
                this.notifyAll();//生产完毕,唤醒所有消费者
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized void pop() {
            try {
                while (isEmpty) {//如果是空的,则先需要生产者生产,才能消费
                    this.wait();
                }
                //---消费数据开始----
                Thread.sleep(10l);
                System.out.println("name---" + name + "," + "age---" + age);
                //----消费数据结束----
                isEmpty = true;
                this.notifyAll();//消费完毕,唤醒所有生产者
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    生产者:

    public class Producer implements Runnable {
        private Person person;
    
        public Producer(Person person) {
            this.person = person;
        }
    
        @Override
        public void run() {
            //生产对象
            for (int i = 0; i < 10; i++) {
                if (i % 2 == 0) {
                    person.push("Tom", 10);
                    System.out.println("i is " + i + " Tom");
                } else {
                    person.push("Lily", 20);
                    System.out.println("i is " + i + " Lily");
                }
            }
        }
    }

    消费者:

    public class Consumer implements Runnable {
        private Person person;
    
        public Consumer(Person person) {
            this.person = person;
        }
    
        @Override
        public void run() {
            //消费对象
            this.person.pop();
        }
    }

    测试:

    //生产者与消费者模式:生产者生产一次数据,就暂停生产者线程,等待消费者消费;消费者消费完了,消费者线程暂停,等待生产者生产数据
            //同步锁池:同步锁必须选择多个线程共同的资源对象,而一个线程获得锁的时候,其他线程都在同步锁池获取锁;当那个线程释放
            //同步锁后,其他线程开始由CPU调度分配锁
            Person person = new Person();//多个线程共享的资源
            for (int i = 0; i < 50; i++) {
                Thread thread = new Thread(new Producer(person));
                thread.start();
                Thread thread1 = new Thread(new Consumer(person));
                thread1.start();
            }

    wait():执行该方法的线程对象,释放同步锁,JVM会把该线程放到等待池中,等待其他线程唤醒该线程

    notify():执行该方法的线程唤醒在等待池中等待的任意一个线程,把线程转到锁池中等待(注意锁池和等待池的区别)

    notifyAll():执行该方法的线程唤醒在等待池中等待的所有线程,把线程转到锁池中等待。

    假设 A 线程和 B 线程同时操作一个 X 对象,A,B 线程可以通过 X 对象的 wait() 和 notify() 方法来进行通信,流程如下:

    ①、当线程 A 执行 X 对象的同步方法时,A 线程持有 X 对象的 锁,B线程在 X 对象的锁池中等待

    ②、A线程在同步方法中执行 X.wait() 方法时,A线程释放 X 对象的锁,进入 X 对象的等待池中

    ③、在 X 对象的锁池中等待锁的 B 线程获得 X 对象的锁,执行 X 的另一个同步方法

    ④、B 线程在同步方法中执行 X.notify() 方法,JVM 把 A 线程从等待池中移动到 X 对象的锁池中,等待获取锁

    ⑤、B 线程执行完同步方法,释放锁,等待获取锁的 A 线程获得锁,继续执行同步方法

  • 相关阅读:
    MyBatis自带的逆向工程
    StringUtils.isNotEmpty和StringUtils.isNotBlank的区别
    JS根据身份证号码精确计算年龄和性别
    java根据生日精确计算年龄
    单列模式
    Java事务处理
    数据库连接池
    CRM(四川网脉系统)项目总结
    流的文件操作(File)
    Java的关键字与标识符
  • 原文地址:https://www.cnblogs.com/BonnieWss/p/10439888.html
Copyright © 2020-2023  润新知