• 多线程


    1.同步方法和非同步方法是否可以同时调用

    可以同时调用,可以理解为不加synchronized的方法无视这个对象的锁

        int count=10;
        public synchronized void m1(){
            count--;
            System.out.println(Thread.currentThread().getName()+"m1 started 01 : "+count);
            try {
                Thread.sleep(10000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"m1 started 02 : "+count);
        }
        public /*synchronized*/ void m2(){
            try {
                count--;
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"m2 start ..."+count);
        }
        public static void main(String[] args) {
            Test t = new Test();
            new Thread(()->t.m1(),"m1").start();
            new Thread(()->t.m2(),"m2").start();
        }

    2.对业务写方法上枷锁,业务读方法上不加锁,可能会产生脏读现象(读到在写的过程中还没有完成的数据)

         String name;
         Double price;
        public synchronized void set(String name,Double price){
            this.name=name;
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.price=price;
        }
        public Double get(){
            return this.price;
        }
        public static void main(String[] args) {
            Test t = new Test();
            new Thread(()->t.set("zhangsan",100.00),"m1").start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(t.get());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(t.get());
        }

    3.一个同步方法可以调用另外一个同步方法,

    一个线程已经拥有了某个对象的锁,再次申请的时候仍然会得到该对象的锁,也就是说synchronized获得的锁是可以重入的,子类也可以调用父类的同步方法

    4.程序在执行过程中,如果出现异常

    程序执行过程中如果出现异常,默认情况锁会被释放

    所以在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况,若是不想释放这个锁则加上try()catch()

    5.volatile

    这个和JMM有关(Java Memory Model)Java内存模型

    在JMM中有一个内存为主内存(我们所说的栈内存、堆内存都可以理解为主内存),每一个线程在执行的过程中都有一个自己的内存,存放自己变量的内存。它会从主内存中读取数据,把数据放进自己的缓冲区内,然后进行操作,然而在没操作完之前,它不会再回到主内存中取读取数据,因为本地已经缓存了数据,操作完之后再写回主内存中。若是读的这条数据加上volatile,这条数据发生变化之后,就会通知其他线程,之前的数据过期了,请重新来主内存中读取数据。

    注:volatile不能替代synchronized,volatile只能保证可见性,synchronized技能保证可见性,还能保证原子性,synchronized性能比volatile低

     volatile boolean running=true;
        void m(){
            System.out.println("m start");
            while (running){
            }
            System.out.println("m end");
        }
    
        public static void main(String[] args) {
            Test3 t = new Test3();
            new Thread(()->t.m(),"m1").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            t.running=false;
        }

    6.AtomicInteger

    AtomicXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的。

    7.细粒度的锁效率要比粗粒度的锁效率高

    8.synchronized所对象

    所是锁的是对象,则对的是堆内存中的对象,若是新new了一个对象,则这个对象已经不是原来的对象了,所以不能新new 这个对象。

    9.不要以字符串常量作为锁的对象 

    10.wait、sleep、notify

    wait释放锁,sleep不释放锁,notify也不释放锁。wait和notify都必须在synchronized同步代码块中使用,同样也是争夺的对象上的wait和notify

    public class Test4 {
        List list = new ArrayList();
        public void add(Object obj){
            list.add(obj);
        }
        public int size(){
            return list.size();
        }
    
        public static void main(String[] args) {
            Test4 t = new Test4();
            final Object obj = new Object();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (obj){
                        System.out.println("线程2启动");
                        if(t.size()!=5){
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("线程2结束");
                        obj.notify();
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (obj){
                        System.out.println("线程1启动");
                        for (int i=0;i<10;i++){
                            System.out.println("add :"+i);
                            t.add(obj);
                            if (t.size()==5){
                                obj.notify();
                                try {
                                    obj.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }).start();
        }
    }

     11.CountDownLatch

    使用Latch(门闩)代替wait notify 来进行通知,好处是通信方式简单,同时也可以指定等待时间,使用await和countdown方法代替wait和notify,CountDownLatch不涉及锁定,当count的值为零时(每一次执行countdown方法时都会减一),当前线程继续执行。当不涉及同步,只是涉及线程通信的时候,用CountDownLatch。

    public class Test4 {
        List list = new ArrayList();
        public void add(Object obj){
            list.add(obj);
        }
        public int size(){
            return list.size();
        }
    
        public static void main(String[] args) {
            Test4 t = new Test4();
            CountDownLatch latch = new CountDownLatch(1);
            new Thread(new Runnable() {
                @Override
                public void run() {
                        System.out.println("线程2启动");
                        if(t.size()!=5){
                            try {
                                latch.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("线程2结束");
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                        System.out.println("线程1启动");
                        for (int i=0;i<10;i++){
                            System.out.println("add :"+i);
                            t.add(latch);
    
                            if (t.size()==5){
                                latch.countDown();
                            }
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                    }
                }
            }).start();
        }
    }

     12.ReenTrantLock

    使用reentrantlock同样可以锁住对象,需要注意的是reentrantlock必须要必须要必须要手动释放锁

    使用syn锁定的话若果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放

    注:这个用起来比较灵活,他有一个方法为:lock.trylock(); 返回的是布尔类型的值,拿着锁会怎样执行,没有拿着锁另一种方式执行,这样可以进行判断,还可以给他指定一个特定的时间,lock.trylock(5,TimeUtil.SECCONDS),指定5秒后。ck.lockInterruptibly()也可以打断自己等锁,去执行其他的任务,打断方法为t2.interrupt();Lock lock = new ReentrantLock(true)这个是公平锁

    public class Test6 {
        Lock lock = new ReentrantLock();
        public void m1(){
            lock.lock();
            System.out.println("线程 m1 启动");
            try {
                for (int i=0;i<5;i++){
                    System.out.println("do : "+i);
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        public void m2(){
            lock.lock();
            System.out.println("线程 m2 启动");
            lock.unlock();
        }
    
        public static void main(String[] args) {
            Test6 t = new Test6();
            new Thread(t::m1).start();
            new Thread(t::m2).start();
        }

    /*public void m2(){
    Boolean locked=false;
    try {
    locked = lock.tryLock(2,TimeUnit.SECONDS);
    if (locked){
    System.out.println("线程 m2 拿到锁 并 启动");
    }else {
    System.out.println("线程 m2没有拿到锁 启动");
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }finally {
    if (locked)lock.unlock();
    }
    }*/
    }


    打断线程的等待

    public class Test6 {
        Lock lock = new ReentrantLock(true);
        public void m1(){
            lock.lock();
            System.out.println("线程 m1 启动");
            try {
                for (int i=0;i<5;i++){
                    System.out.println("do : "+i);
                    SECONDS.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    
        public void m2(){
            try {
    //            lock.lock();
                System.out.println("线程2 等待获取锁");
                //若是长时间拿不到锁,自己停止等待,继续执行其他程序
                lock.lockInterruptibly();
    //            lock.tryLock(2, SECONDS);
    //            SECONDS.sleep(2);
                System.out.println("正常执行");
            } catch (InterruptedException e) {
                System.out.println("线程2不再等待锁,去执行其他任务");
            }finally {
                try{
                    lock.unlock();
                }catch (Exception e){
                    System.out.println("没有获取锁");
                }
            }
    
        }
    
        public static void main(String[] args) {
            Test6 t = new Test6();
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    t.m1();
                }
            });
            thread1.start();
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    t.m2();
                }
            });
            thread2.start();
            try {
                SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread2.interrupt();
        }
    }
  • 相关阅读:
    基于socket.io的实时消息推送
    mysql_use_result & mysql_store_result & MYSQLI_ASYNC
    深入浅出讲解:php的socket通信
    Mysql时间存储类型优缺点?DATETIME?TIMESTAMP?INT?
    PHP垃圾回收机制引用计数器概念
    php调试函数
    Docker生产环境实践指南
    11 个 Linux 上最佳的图形化 Git 客户端
    浅谈TCP/IP网络编程中socket的行为
    highcharts 使用实例
  • 原文地址:https://www.cnblogs.com/gxlaqj/p/11691190.html
Copyright © 2020-2023  润新知