zlb820的gravatar头像
zlb820 2017-03-15 18:12:23
Java多线程实现方法

第一篇博客:

缘由:这两天看了个笔试题。

原题是什么记不清了,只记得 大概,说一下吧:

问: 请实现思个线程,两个个线程对数字加一,两个线程减一?

   题目很简单,答案却不止一个。我想怎么用不同种多线程的方法实现呢。。。

不贴实现,只给知识点,自己写。。有点多,慢慢看,不着急

--------------------------------------------------------------------------------------------

 

 java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 

    将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用, 

    从而保证了该变量的唯一性和准确性。

 

1.同步方法 

    即有synchronized关键字修饰的方法。 

    由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 

    内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

    代码如: 

    public synchronized void save(){}

   注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

 

2.同步代码块 

    即有synchronized关键字修饰的语句块。 

    被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

    代码如: 

    synchronized(object){ 

    }

    注:同步是一种高开销的操作,因此应该尽量减少同步的内容。 

    通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。 

     

    代码实例: 

package com.x敏感词hread;

 

    /**

     * 线程同步的运用

     * 

     * @author XIEHEJUN

     * 

     */

    public class SynchronizedThread {

 

        class Bank {

 

            private int account = 100;

 

            public int getAccount() {

                return account;

            }

 

            /**

             * 用同步方法实现

             * 

             * @param money

             */

            public synchronized void save(int money) {

                account += money;

            }

 

            /**

             * 用同步代码块实现

             * 

             * @param money

             */

            public void save1(int money) {

                synchronized (this) {

                    account += money;

                }

            }

        }

 

        class NewThread implements Runnable {

            private Bank bank;

 

            public NewThread(Bank bank) {

                this.bank = bank;

            }

 

            @Override

            public void run() {

                for (int i = 0; i < 10; i++) {

                    // bank.save1(10);

                    bank.save(10);

                    System.out.println(i + "账户余额为:" + bank.getAccount());

                }

            }

 

        }

 

        /**

         * 建立线程,调用内部类

         */

        public void useThread() {

            Bank bank = new Bank();

            NewThread new_thread = new NewThread(bank);

            System.out.println("线程1");

            Thread thread1 = new Thread(new_thread);

            thread1.start();

            System.out.println("线程2");

            Thread thread2 = new Thread(new_thread);

            thread2.start();

        }

 

        public static void main(String[] args) {

            SynchronizedThread st = new SynchronizedThread();

            st.useThread();

        }

 

    }

     

3.使用特殊域变量(volatile)实现线程同步

    a.volatile关键字为域变量的访问提供了一种免锁机制, 

    b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新, 

    c.因此每次使用该域就要重新计算,而不是使用寄存器中的值 

    d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量 

    例如: 

        在上面的例子当中,只需在account前面加上volatile修饰,即可实现线程同步。 

代码示例:

      //只给出要修改的代码,其余代码与上同

        class Bank {

            //需要同步的变量加上volatile

            private volatile int account = 100;

 

            public int getAccount() {

                return account;

            }

            //这里不再需要synchronized 

            public void save(int money) {

                account += money;

            }

        }

 

    注:多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。 

    用final域,有锁保护的域和volatile域可以避免非同步的问题。 

    

4.使用重入锁实现线程同步

 

    在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。 

    ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 

    它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力

    ReenreantLock类的常用方法有:

 

        ReentrantLock() : 创建一个ReentrantLock实例 

        lock() : 获得锁 

        unlock() : 释放锁 

    注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用 

        

    例如: 

        在上面例子的基础上,改写后的代码为: 

    代码实例:

 

//只给出要修改的代码,其余代码与上同

        class Bank {

            

            private int account = 100;

            //需要声明这个锁

            private Lock lock = new ReentrantLock();

            public int getAccount() {

                return account;

            }

            //这里不再需要synchronized 

            public void save(int money) {

                lock.lock();

                try{

                    account += money;

                }finally{

                    lock.unlock();

                }

                

            }

}

          

    注:关于Lock对象和synchronized关键字的选择: 

        a.最好两个都不用,使用一种java.util.concurrent包提供的机制, 

            能够帮助用户处理所有与锁相关的代码。 

        b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码 

        c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁 

        

5.使用局部变量实现线程同步 

    如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本, 

    副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

    ThreadLocal 类的常用方法

    ThreadLocal() : 创建一个线程本地变量 

    get() : 返回此线程局部变量的当前线程副本中的值 

    initialValue() : 返回此线程局部变量的当前线程的"初始值" 

    set(T value) : 将此线程局部变量的当前线程副本中的值设置为value

    例如: 

        在上面例子基础上,修改后的代码为:

    代码实例: 

 

//只改Bank类,其余代码与上同

        public class Bank{

            //使用ThreadLocal类管理共享变量account

            private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){

                @Override

                protected Integer initialValue(){

                    return 100;

                }

            };

            public void save(int money){

                account.set(account.get()+money);

            }

            public int getAccount(){

                return account.get();

            }

        }

 

    注:ThreadLocal与同步机制 

        a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。 

        b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式 

 

java 中每个对象都有一把锁, 线程可以通过 synchronized 关键字来获取对象上的锁

同步方法(粗粒度锁):

        1. 修饰一般方法: public synchronized void method (){...}, 获取的是当前调用 对象this 上的锁

        2. 修饰静态方法: public static synchronized void method (){...}, 获取当前类的 字节码对象上的锁

同步代码块(细粒度锁):

         synchronized ( obj ) {...}, 同步代码块可以指定获取哪个对象上的锁, obj 任意


打赏
最近浏览
韩毅飞  LV11 2019年4月13日
JoyKinG  LV19 2019年3月13日
梦醒繁华丶  LV19 2018年4月27日
zhizaideren  LV2 2018年1月19日
coding喵  LV16 2017年11月19日
gudumingjian  LV1 2017年10月10日
liusi331  LV2 2017年9月13日
203778513  LV9 2017年9月13日
q2823865 2017年9月7日
暂无贡献等级
zhaoshuting  LV5 2017年9月4日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友