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

多线程相关一些知识

作者: 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;
    }

}

相关文章

  • Java高效并发(九)

    一段时间没有回顾多线程相关知识了,虽然工作中会用到一些多线程的内容,但都偏向于基础,今天重读多线程相关内容,发现有...

  • 多线程相关一些知识

    Runnable 和 Thread的关系 Runnable是接口,Thread是一个类 Thread是实现了Run...

  • 多线程相关知识

    1、iOS中一般有哪几种?各自的特点是什么? NSOperation 对象在finished之后怎样从queue当...

  • java基础之多线程略解

    java中的多线程是非常重要的一个知识点,下面我们就来简单的介绍下多线程的相关知识以及相关方法。 并发与并行 并行...

  • 多线程

    前言 今天我们主要看看关于线程与进程的一些面试相关的知识点,以及引发出来的多线程的相关知识点。 1. 线程、进程的...

  • iOS多线程

    写一下iOS多线程的相关知识,多线程无论是在实际开发中还是在面试的时候都是相关重要的一个知识点,特别是GCD和NS...

  • iOS 多线程总结(上)

    一、前言 多线程是在 iOS 里非常重要的一块儿知识点,我最近学习了李明杰大神的多线程相关视频,对自己的多线程相关...

  • iOS开发进阶--1.多线程简介

    相关文章链接:1.多线程简介(本文)2.实现多线程的3种方法......待续 学习是由已知的知识模型推理未知的知识...

  • 多线程GCD相关知识

    核心概念: 1任务--执行什么操作 2队列--用来存放任务 同步函数: dispatch_sync(...

  • iOS 多线程之NSOperation、NSOperationQ

    本文用来介绍 iOS 多线程中 NSOperation、NSOperationQueue 的相关知识以及使用方法。...

网友评论

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

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