美文网首页
多线程相关一些知识

多线程相关一些知识

作者: jeffrey要努力 | 来源:发表于2018-04-17 22:22 被阅读5次

    Runnable 和 Thread的关系

    1. Runnable是接口,Thread是一个类
    2. Thread是实现了Runnable接口的类
    3. 尽量用Runnable,因为接口继承比类的继承更加面向对象

    线程锁的方式

    简单的方式就是用synchronized,可以修饰方法,代码块 静态方法,对象
    如果修饰的是静态方法,该类的所有方法都是用的同一把锁
    如果修饰的是非静态的,锁属于这个对象

    更细粒度的控制是使用Lock,ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

    public static void main(String[] args) throws InterruptedException {
            Seller seller = new Seller(20);
            new Thread(seller).start();
            new Thread(seller).start();
            new Thread(seller).start();
    
        }
    
    /**
    ** 在竞争的过程中会出现超卖的情况,
    **    因为while没有锁住,几个线程在锁住票之前已经持有票了,票的数量不能在锁外去读
    **/
    public static class Seller implements Runnable {
        private int tickCount;
    
        public Seller(int count) {
            this.tickCount = count;
        }
    
        @Override
        public void run() {
            while (tickCount >0 ) {
                synchronized (Seller.class) {
                    System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount);
                    tickCount--;
                    /**
                     * 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
                     */
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    打印出来的结果:超卖了一张

    Thread-0 : 卖出票 20
    Thread-0 : 卖出票 19
    Thread-2 : 卖出票 18
    Thread-2 : 卖出票 17
    Thread-1 : 卖出票 16
    Thread-1 : 卖出票 15
    Thread-1 : 卖出票 14
    Thread-2 : 卖出票 13
    Thread-2 : 卖出票 12
    Thread-2 : 卖出票 11
    Thread-2 : 卖出票 10
    Thread-2 : 卖出票 9
    Thread-2 : 卖出票 8
    Thread-2 : 卖出票 7
    Thread-2 : 卖出票 6
    Thread-0 : 卖出票 5
    Thread-0 : 卖出票 4
    Thread-0 : 卖出票 3
    Thread-0 : 卖出票 2
    Thread-0 : 卖出票 1
    Thread-2 : 卖出票 0
    Thread-1 : 卖出票 -1
    

    票的正确数量应该在锁里面读取,这样就不会超卖

     public static class Seller implements Runnable {
            private Integer tickCount;
    
            public Seller(int count) {
                this.tickCount = count;
            }
    
            @Override
            public void run() {
                while (true ) {
                    //所有对象公用一把锁
                    synchronized (Seller.class) {
                        //票的判断放到锁里面
                        if (tickCount < 0){
                            return;
                        }
                        System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount);
                        tickCount--;
                        /**
                         * 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
                         */
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                }
            }
        }
    

    通过Lock实现同步互斥,因为Lock是接口,所以使用它的实现类来ReentrantLock,

    /**
         * 通过Lock来实现同步互斥
         */
        public static class Seller implements Runnable {
            private Integer tickCount;
            private Lock lock;
    
            public Seller(int count) {
                this.tickCount = count;
                this.lock = new ReentrantLock();
            }
    
            @Override
            public void run() {
                while (true ) {
                        try {
                            lock.lock();
                            //票的判断放到锁里面
                            if (tickCount < 0){
                                return;
                            }
                            System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount);
                            tickCount--;
                            /**
                             * 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
                             */
                                Thread.sleep(100);
    
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            lock.unlock();
                        }
    
                    }
    
                }
            }
    

    线程之间的同步

    之前都是互相抢的 太不友好了,现在来了个管理者,让大家一销售一次卖N张,卖完就收工让下个销售来,虽然是多线程,但是同一个时刻只有一个线程在工作,通过condition来实现

        public static class SellerManager{
            private ReentrantLock lock;
            private Condition condition;
    
            public SellerManager(){
                this.lock = new ReentrantLock();
                this.condition = lock.newCondition();
            }
    
            public void start(){
                TicketCount tickerCount = new TicketCount(15);
    
    
                while (tickerCount.getCount() > 0 ){
                    System.out.println("新的销售开始");
                    Seller seller = new Seller(tickerCount,5,lock,condition);
                    new Thread(seller).start();
                    try {
                        lock.lock();
                        //需要放在lock里面
                        System.out.println("开始等待第下一个人介入 " +                     lock.getHoldCount());
                        condition.await();
                        lock.unlock();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        System.out.print(e);
    
                    }
                }
            }
        }
    
        /***
         * 实现一个人卖5张
         */
        public static class Seller implements Runnable {
            private TicketCount tickCount;
    
            private int canSellCount;
            private Lock lock;
            private Condition condition;
    
            public Seller(TicketCount count,int canSellCount,Lock lock,Condition condition) {
                this.tickCount = count;
                this.canSellCount = canSellCount;
                this.lock = lock;
                this.condition = condition;
            }
    
            @Override
            public void run() {
                while (true ) {
                        if (canSellCount == 0){
                            System.out.println(Thread.currentThread().getName() + " 卖票完成 " );
    
                            break;
                        }
                        System.out.println(Thread.currentThread().getName() + " : 卖出票 " + tickCount.getCount());
                        tickCount.sellOne();
                        canSellCount--;
                        /**
                         * 这个放到里面,模拟耗时操作,比如读写数据库,读写本地文件
                         */
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.lock();
                condition.signal();
                lock.unlock();
    
            }
        }
    
        private static class TicketCount{
            private int count;
    
            public TicketCount(int count){
                this.count = count;
            }
    
            public void sellOne(){
                this.count --;
            }
    
            public int getCount() {
                return count;
            }
        }
    }
    

    打印的结果 :

    新的销售开始
    开始等待第下一个人介入 1
    Thread-0 : 卖出票 15
    Thread-0 : 卖出票 14
    Thread-0 : 卖出票 13
    Thread-0 : 卖出票 12
    Thread-0 : 卖出票 11
    Thread-0 卖票完成
    新的销售开始
    开始等待第下一个人介入 1
    Thread-1 : 卖出票 10
    Thread-1 : 卖出票 9
    Thread-1 : 卖出票 8
    Thread-1 : 卖出票 7
    Thread-1 : 卖出票 6
    Thread-1 卖票完成
    新的销售开始
    开始等待第下一个人介入 1
    Thread-2 : 卖出票 5
    Thread-2 : 卖出票 4
    Thread-2 : 卖出票 3
    Thread-2 : 卖出票 2
    Thread-2 : 卖出票 1
    Thread-2 卖票完成
    

    这个修改下就可以实现线程B,C,D等待线程A完成后再进行工作.

    单例的写法

    /**
     * Created by Administrator on 2018/4/17.
     */
    
    
    /**
     * 懒汉,对象是懒加载
     * 线程不安全
     */
    public class User {
        private static User instance;
        private User(){}
    
        public static User getInstance(){
            if (instance == null){
                instance = new User();
            }
            return instance;
        }
    }
    
    
    /**
     * 懒汉,对象是懒加载
     * 使用Synchronized修饰了静态方法,所以线程安全
     * 但是方法级别的锁,效率低
     *
     */
    public class User {
        private static User instance;
        private User(){}
    
        public  static synchronized  User getInstance(){
            if (instance == null){
                instance = new User();
            }
            return instance;
        }
    }
    /**
     * 饿汉
     * 当class文件被加载的时候就创建了对象,获取的时候没有对象创建操作,不存在线程安全问题
     * 如果没用到也占用着内存
     */
    public class User {
        private static User instance = new User();
        private User(){
            System.out.println("user 构造函数");
        }
    
        public  static   User getInstance(){
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("user getInstance");
            return instance;
        }
    }
    
    /**
     * 同上,static代码块也是在类加载的时候运行
     */
    public class User {
        private static User instance = null;
        static {
            instance = new User();
        }
        private User(){}
    
        public  static   User getInstance(){
            return instance;
        }
    }
    
    /**
     * 这个静态内部类为什么可以延迟创建对象不太明白
     */
    public class User {
    
        private static class UserHolder{
            static {
                System.out.println("UserHolder");
    
            }
            private static User user = new User();
        }
    
        private User(){
            System.out.println("user 构造函数");
        }
        static {
            System.out.println("user static");
    
        }
        public  static   User getInstance(){
            System.out.println("user getInstance");
            return UserHolder.user;
        }
    }
    
    /**
     * 利用enum的特性
     * enum里面的对象都是单例的
     * 使用:User.instance.hello();
     */
    public enum User{
        instance;
        public void hello(){
    
        }
    }
    
    /**
     * 双重校验锁
     * 用的最多,检查了2次,第二次用synchronized进行了同步,因为只是对代码块修饰,比直接修饰方法效率高一点
     */
    public class User{
        private static User user = null;
    
        private User(){}
    
        public static User getInstance(){
            if (user == null){
                synchronized (User.class){
                    if (user == null){
                        user = new  User();
                    }
                }
            }
            return user;
        }
    
    }
    

    相关文章

      网友评论

          本文标题:多线程相关一些知识

          本文链接:https://www.haomeiwen.com/subject/yucmkftx.html