zoukankan      html  css  js  c++  java
  • 线程锁、定时器

    一、线程同步安全锁(关键在于同步监听对象要一致,同步监听对象看作是锁,也就是说多个对象要使用的是同一把锁

    1、synchronized同步代码块锁(继承类线程和实现接口类线程都适用)

    语法结构:synchronized(同步对象){

       需要被锁住的代码(也就是容易发生线程安全的代码)

    }

    同步对象:指的是同步监听对象,看作是是一把锁

    示例代码:

     1 /**
     2  * 解决线程同步安全问题最关键的是要使用同一把锁,也就是同一监听对象。
     3  * 
     4  *
     5  */
     6 public class Test1 {
     7     public static void main(String[] args) {
     8         TicketThread t1 = new TicketThread("梓沐");
     9         TicketThread t2 = new TicketThread("盂梦");
    10         TicketThread t3 = new TicketThread("哈哈");
    11         t1.start();
    12         t2.start();
    13         t3.start();
    14     }
    15 
    16 }
    17 /**
    18  * 解决线程安全问题方法之一:synchronized同步代码块
    19  * 语法结构:
    20  *         synchronized(同步对象){
    21  *             需要被锁住的代码
    22  *         }
    23  *         同步对象:简单理解就是一把锁
    24  *         注意:多个对象拿到的必须是同一把锁
    25  *         我们在填充同步对象的时候,使用了new Object()也使用了this,发现都不可以,因为创建了3个对象
    26  *        所以3个线程拿到的锁都不是同一把,而是3把不同的锁,所以是没有解决线程安全问题的
    27  *        怎么解决呢?
    28  *        答:使用字节码对象可以解决
    29  *        字节码对象   简单理解   类型.class
    30  *        以.java为后缀的文件   我们称为源文件
    31  *        以.class为后缀的文件  我们称为字节码文件
    32  *        把字节码文件放到jvm中     在jvm中它会存在一个字节码对象,就是你当前类型.class
    33  *        字节码对象它是单例的,它在jvm中永远只会存在一份
    34  *
    35  */
    36 public class TicketThread extends Thread{
    37     static int num = 2000;
    38     public TicketThread(String string) {
    39         super(string);
    40     }
    41     @Override
    42     public void run() {
    43     //当synchronized锁住了整个while循环时,只有一个线程能进去,当切换到其它线程时,其他线程也不能进入,因此会只有一个窗口买票。
    44             while(num>0){
    45     /*
    46      * /当synchronized锁住了while循环中的执行语句时,此时会出现0票、-1票,原因是ABC三个线程都能进入while循环,
    47      * 然后当A线程进入执行语句后,执行语句被锁上,此时A线程打印1,然后解锁执行语句,C线程进入,锁上执行语句,C线程打印0,
    48      * 然后解锁执行语句,B线程进入,然后锁上执行语句,此时,B线程打印-1,租后解锁执行语句。因此,解决的办法是在同步代
    49      * 码块里面加上if判断语句。
    50      */
    51                 synchronized (TicketThread.class) {
    52                     if(num>0){
    53                         System.out.println(this.getName()+":"+num);
    54                         num--;
    55                     }
    56             
    57                 }
    58             }
    59     }
    60 
    61 }
    View Code

     

    2、synchronized同步方法

    /**

    * 同步方法相关的同步监听对象问题:

    * 如果你的方法是一个实例方法,那你同步监听对象就是当前类的对象this

    * 如果你的方法是类的方法,那你同步监听对象就是当前类的字节码对象

    * 字节码对象在jvm中永远只有1份

    * 如果你实现的多线程,你是使用的继承方式,那就不能使用同步方法(因为在*类方法里面不能使用实例方法,也就是说在继承方式实现多线程时,我们不能  *知道线程的名称,因此不能满足我们的需求)

    *必须使用同步代码块,因为同步方法不能解决我们的需求

    */

    示例代码:

    public class Test1 {
        public static void main(String[] args) {
            TicketThread t = new TicketThread();
            Thread t1 = new Thread(t,"梓沐");
            Thread t2 = new Thread(t,"盂梦");
            Thread t3 = new Thread(t,"哈哈");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    //解决线程安全问题之synchronized同步方法
    public class TicketThread implements Runnable{
        int num = 50;
        @Override
        public void run() {
            while(num>0){
                saleTicket();
            }
        }
        private synchronized void saleTicket() {
            if(num>0){
                System.out.println(Thread.currentThread().getName()+num);
                num--;
            }
    /**
     * 同步方法相关的同步监听对象问题:
     *             如果你的方法是一个实例方法,    那你同步监听对象就是当前类的this
     *             如果你的方法是类的方法,那你同步监听对象就是当前类的字节码对象
     *             字节码对象在jvm中永远只有1份
     * 如果你实现的多线程,你是使用的继承方式,那就不能使用同步方法(因为在类方法里面不能使用实例方法,也就是说在继承方式实现多线程时,
     * 我们不能知道线程的名称,因此不能满足我们的需求)
     *必须使用同步代码块,因为同步方法不能解决我们的需求
     */    
        }
    }
    View Code

     

    3、Lock加锁的方法(Lock是一个接口)

    因为lock是一个接口,因此不能直接实例化,所以只能实例化它的子类ReentrantLock类

    示例代码:

    public class Test1 {
        public static void main(String[] args) {
            TicketThread t1 = new TicketThread("梓沐");
            TicketThread t2 = new TicketThread("盂梦");
            TicketThread t3 = new TicketThread("哈哈");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    /**解决线程安全问题:
    *            解决线程同步方式三:使用Lock方式
    *                语法格式:
    *                    Lock lock = new ReentrantLock()   生成一个lock对象
    *                    lock.lock();//上锁
    *                    try{
    *                        需要被锁住的代码
    *                    }finally{
    *                        lock.unlock();//释放锁资源
    *                    }
    *
    *            线程同步前提: 多个线程要共享一把锁
    */
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TicketThread extends Thread{
        static Lock lock = new ReentrantLock();
        static int num = 2000;
        public TicketThread(String string) {
            super(string);
        }
        @Override
        public void run() {
            while(num>0){
                lock.lock();
    //同步监听对象就是lock对象
                try {
                    if(num>0){
                        System.out.println(getName()+num);
                        num--;
                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
    View Code

    二、join方法

    1、void join() 当某个线程加入之后,其他线程就必须等着,一直要等着加入的线程执行完毕之后,其他线程才能继续执行

    三、守护线程

      1守护线程/精灵线程/后台线程:每个线程都可以或不可以标记为一个守护程序

       2后台线程仅仅就是对线程的一个分类或者标记。

       3后台线程作用:后台线程主要是给前台线程做服务的

       示例:其实垃圾回收线程就是一个后台线程

       4后台线程特点: 当前台线程死亡之后,后台线程会自动死亡(注意:如果前台线程死了之后,后台线程不一定马上死亡 (因为很可能处理后事))

       5后台线程相关的方法:

       boolean isDaemon() 测试该线程是否为守护线程。

        void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。

       6主线程:

       默认是前台线程  

       主线程只能是前台线程不能更改为后台线程

      注意:当一个线程处于运行状态的时候,是不能更改它为后台线程的

       7自定义线程:

       默认是后台线程还是前台线程是和它创建的环境线程完全一致

      线程的优先级是和创建它的环境线程是一致的

    四、线程通信和唤醒线程

    1、线程的生命周期(创建线程对象、启动线程、运行线程、死亡(线程执行完毕或者异常没处理好))

    2、线程通信指的是线程之间共享资源

    3、synchronized同步方法下的线程通信和唤醒线程

    代码实例:

    public class Test1 {
        public static void main(String[] args) {
            Account account = new Account(0);
            SaveMoney saveMoney = new SaveMoney(account);
            GetMoney getMoney = new GetMoney(account);
            saveMoney.start();
            getMoney.start();
        }
    }
    public class SaveMoney extends Thread{
        private Account account;
    
        public SaveMoney(Account account) {
            super();
            this.account = account;
        }
        @Override
        public void run() {
            for (int i = 1; i <= 12; i++) {
                try {
                    account.saveMoney(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    public class GetMoney extends Thread{
        private Account account;
    
        public GetMoney() {
            super();
        }
    
        public GetMoney(Account account) {
            super();
            this.account = account;
        }
        @Override
        public void run() {
            for (int i = 1; i <= 12; i++) {
                try {
                    account.getMoney(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    
    }
    public class Account{
        private int money;
        private boolean empty = true;//定义一个字段来标志是否有钱
        public Account(int money) {
            super();
            this.money = money;
        }
    
        public Account(){
            
        }
        
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    
        public synchronized void saveMoney(int i) throws InterruptedException {
            if(!empty){
                this.wait();
            }
            System.out.println("存钱之前=========="+money);
            money += i;
            System.out.println("存钱之后=========="+money);
            empty = false;
            this.notify();
            
        }//两个方法的同步监听对象(锁)是Account类的实例对象。
    
        public synchronized void getMoney(int i) throws InterruptedException {
            if(empty){
                this.wait();
            }
            System.out.println("取钱之前=========="+money);
            money -= i;
            System.out.println("取钱之后=========="+money);
            empty = true;
            this.notify();
            
        }
        
    
    }
    View Code

    4、lock方法下的线程通信和唤醒线程

    示例代码:

    public class Test1 {
        public static void main(String[] args) {
            Account account = new Account(0);
            SaveMoney saveMoney = new SaveMoney(account);
            GetMoney getMoney = new GetMoney(account);
            Thread t1= new Thread(saveMoney);
            Thread t2 = new Thread(getMoney);
            t1.start();
            t2.start();
        }
    }
    public class SaveMoney implements Runnable{
        private Account account;
        
        public SaveMoney(Account account) {
            this.account = account;
        }
        @Override
        public void run() {
            for (int i = 1; i <= 12; i++) {
                account.saveMoney(1000);
            }
        }
    }
    public class GetMoney implements Runnable{
        private Account account;
        public GetMoney(Account account) {
            this.account = account;
        }
        @Override
        public void run() {
            for (int i = 1; i <=12; i++) {
                account.getMoney(1000);
            }
        }
    }
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    public class Account {
        private int money;
        private boolean empty = true;
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        //返回一个与此lock实例一起使用的condition实例,Condition对象替换了Object类的wait和notify方法
        /*
         * 使用lock时,同步监听对象就是lock。
         * 线程通信与唤醒线程指的是synchronizesd同步方法中使用Object的wait方法(成为等待线程),
         * 或者notify方法(唤醒线程)
         * 或者lock方法中使用condition对象中的await方法和single方法。
         * 注意:notify方法和single方法都只能一次唤醒一个线程
         */
        public Account() {
        }
        public Account(int money) {
            this.money = money;
        }
        public int getMoney() {
            return money;
        }
        public void setMoney(int money) {
            this.money = money;
        }
        public void saveMoney(int i) {
            lock.lock();
            try {
                if(!empty){
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("======存钱之前"+money);
                this.money+=i;
                System.out.println("======存钱之后"+money);
                empty = false;
                condition.signal();
            } finally {
                lock.unlock();
            }
        }
        public void getMoney(int i) {
            lock.lock();
            try {
                if(empty){
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("=====取钱之前"+money);
                this.money-=i;
                System.out.println("=====取钱之后"+money);
                empty = true;
                condition.signal();
            } finally {
                condition.signal();
            }
        }
    }
    View Code

     

    五、Timer定时器

    代码实例:

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Timer;
    import java.util.TimerTask;
    public class Test1 {
        public static void main(String[] args) throws ParseException {
            //Timer() 创建一个新计时器。
            Timer timer = new Timer();
            //void schedule(TimerTask task, Date time)  安排在指定的时间执行指定的任务。 如果时间已过,启动之后马上执行
            String str = "2019-04-09 11:36:00";
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            
            timer.schedule(new TimerTask(){
    //此处使用了匿名内部类,这样可以少些很多代码。匿名内部类只能new接口或者抽象类,由于抽象类和接口不能实例化,所以实际上new的是匿名内部类的对象
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    System.out.println("定时炸弹");
                }
                
            }, sdf.parse(str));
            // void schedule(TimerTask task, Date firstTime, long period)  在指定的firstTime执行指定的task任务,
            //以后每个period时间重复执行,如果时间已过,启动之后,立刻执行,并且每隔period时间重复执行,不会把之前没有执行的代码执行。
            timer.schedule(new TimerTask(){
    
                @Override
                public void run() {
                    System.out.println("我在指定时间执行");        
                }    
            }, sdf.parse(str), 2000);
            //void schedule(TimerTask task, long delay, long period)   延迟delay时间执行task任务,每隔period时间又重复执行task任务
            //如果时间已过,启动之后,立刻执行,并且每隔period时间重复执行,不会把之前没有执行的代码执行。
            timer.schedule(new TimerTask(){
    
                @Override
                public void run() {
                    System.out.println("我在指定的延迟时间执行");    
                }
            }, sdf.parse(str), 2000);
            /*
             * void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 在firstTime的时间第一次执行task任务,以后每隔
             * period时间重复执行task任务, 如果时间已过,它会把之前没有执行的代码,全部执行一次
             */timer.scheduleAtFixedRate(new TimerTask(){
                 @Override
                public void run() {
                    System.out.println("我会把之前没执行过的代码全部执行");    
                }
             }, sdf.parse(str), 2000);
        }
    }
    View Code
  • 相关阅读:
    【转】 测试人员的职业规划 --整理标注
    关于数据准备
    功能点算法及在软件测试中的应用
    MySQL常用命令大全
    Python学习笔记
    python 学习笔记 if语句
    一个男人关心的东西 决定了他的层次
    Oracle 计算两个时间的差值
    javascript对下拉列表框(select)的操作
    java需要掌握内容、核心不断更新中
  • 原文地址:https://www.cnblogs.com/limengkun/p/10680200.html
Copyright © 2011-2022 走看看