夜光:Java成神之路(九)擅长的语言

夜光序言:

 

 

学会去看透一切的谜题.
Learning to see the puzzle in everything.
它们无处不在.
They're everywhere.
一旦你开始寻找,就不可能停下了
Once you start looking, it's impossible to stop.
凑巧的是 人们 以及可以体现他们所作所为的谎言和错觉,是最引人入胜的谜题。
It just so happens people,and delusions that inform everything they do,tend to be the most fascinating puzz

《福尔摩斯:基本演绎法》

 

 

 

 
 
正文:
 
                                              以道御术 / 以术识道



1. ThreadLocal 的使用方式

 

(1) 在关联数据类中创建 private static ThreadLocal

 
在下面的类中,私有静态 ThreadLocal 实例(serialNum)为调用该类的静态 SerialNum.get() 方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。
 
 
(线程的序列号是在第一次调用 SerialNum.get() 时分配的,并在后续调用中不会更改。)

 

另一个例子,也是私有静态 ThreadLocal 实例:

package com.hy.多线程高并发;

public class ThreadContext {


    private String userId;
    private Long transactionId;


    private static ThreadLocal threadLocal = new ThreadLocal() {
        @Override
        protected ThreadContext initialValue() {
            return new ThreadContext();
        }
    };

    public static ThreadContext get() {
        return (ThreadContext) threadLocal.get(); //强转一下
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Long getTransactionId() {
        return transactionId;
    }

    public void setTransactionId(Long transactionId) {
        this.transactionId = transactionId;
    }
}
补充:在 JDK 的 API 对 ThreadLocal 私有化的说明。并举例‘线程唯一标识符’
 
UniqueThreadIdGenerator ,大家学习是可以结合官方 API 来学习。

 

 


2. 在 Util 类中创建 ThreadLocal

 

这是上面用法的扩展,即把 ThreadLocal 的创建放到工具类中。

package com.hy.多线程高并发;


public class HibernateUtil {
    
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    private static final SessionFactory sessionFactory; //定义 SessionFactory

    static {
        try {
// 通过默认配置文件 hibernate.cfg.xml 创建 SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            log.error("初始化 SessionFactory 失败!", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    //创建线程局部变量 session,用来保存 Hibernate 的 Session
    public static final ThreadLocal session = new ThreadLocal();

    /**
     * 获取当前线程中的 Session
     *
     * @return Session
     * @throws HibernateException
     */
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
// 如果 Session 还没有打开,则新开一个 Session
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s); //将新开的 Session 保存到线程局部变量中
        }
        return s;
    }

    public static void closeSession() throws HibernateException {
//获取线程局部变量,并强制转换为 Session 类型
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}

 


3. 在 Runnable 中创建 ThreadLocal

 

在线程类内部创建 ThreadLocal,基本步骤如下:

 
①、在多线程的类(如 ThreadDemo 类)中,创建一个 ThreadLocal 对象 threadXxx,用来保存线程间需要隔离处理的对象 xxx。
 
 
②、在 ThreadDemo 类中,创建一个获取要隔离访问的数据的方法 getXxx(),在方法中判断,若ThreadLocal 对象为 null 时候,应该 new()一个隔离访问类型的对象,并强制转换为要应用的类型
 
 
③、在 ThreadDemo 类的 run()方法中,通过调用 getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。
 
 
package com.hy.多线程高并发;

import java.util.Random;

public class ThreadLocalTest implements Runnable{

    ThreadLocal<Studen> studenThreadLocal = new ThreadLocal<Studen>();

    @Override
    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName + " is running...");
        Random random = new Random();
        int age = random.nextInt(100);
        System.out.println(currentThreadName + " is set age: " + age);
        Studen studen = getStudent();
        //通过这个方法,为每个线程都独立的 new 一个 student 对象,每个线程的的
        //student 对象都可以设置不同的值
        studen.setAge(age);
        System.out.println(currentThreadName + " is first get age: " + studen.getAge());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println( currentThreadName + " is second get age: " + studen.getAge());
    }
    private Studen getStudent() {
        Studen studen = studenThreadLocal.get();
        if (null == studen) {
            studen = new Studen();
            studenThreadLocal.set(studen);
        }
        return studen;
    }
    public static void main(String[] args) {
        ThreadLocalTest t = new ThreadLocalTest();
        Thread t1 = new Thread(t,"Thread A");
        Thread t2 = new Thread(t,"Thread B");
        t1.start();
        t2.start();
    }
}


class Studen {
    int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

( 5 ) 多线程共享数据

 

在 Java 传统线程机制中的共享数据方式,大致可以简单分两种情况:

 
多个线程行为一致,共同操作一个数据源
 
也就是每个线程执行的代码相同,可以使用同一个 Runnable 对象,这个 Runnable 对象中有那个共享数据,例如,卖票系统就可以这么做。
 
 
多个线程行为不一致,共同操作一个数据源 。也就是每个线程执行的代码不同 这时候需要用不同的Runnable 对象。
 
例如,银行存取款。
 
 
下面我们通过两个示例代码来分别说明这两种方式。
 

 

1. 多个线程行为一致共同操作一个数据

 
如果每个线程执行的代码 相同 可以使用同一个 Runnable 对象,这个 Runnable 对象中有那个共享数据,例如,买票系统就可以这么做
 
 
package com.hy.多线程高并发;

/**
* @Description:   多个线程行为一致共同操作一个数据
 * 如果每个线程执行的代码相同,可以使用同一个 Runnable 对象
 * 这个 Runnable 对象中有那个共享数据,例如
 * 买票系统就可以这么做
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/
public class Test5 {


    /**
     *共享数据类
     **/
    static class ShareData{
        private int num = 10 ;
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    /**
     *多线程类
     **/
    static class RunnableCusToInc implements Runnable{

        private ShareData shareData;
        public RunnableCusToInc(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }
    /**
     *测试方法
     **/
    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
        }
    }
}




 
 

2. 多个线程行为不一致共同操作一个数据

 
如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对象之间的数据共享:

 

1) 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

 
 
package com.hy.多线程高并发;

/** 
* @Description:
 * 2. 多个线程行为不一致共同操作一个数据
 * 如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对
 * 象之间的数据共享:
 * 1) 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享
 * 数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
* @Param:  
* @return:  
* @Author: Hy
* @Date: 2019
*/ 
public class Test6 {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            if(i%2 == 0){
                new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
            }else{
                new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();
            }
        }
    }

    //封装共享数据类
    static class RunnableCusToInc implements Runnable{
        //封装共享数据
        private ShareData shareData;
        public RunnableCusToInc(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }

    //封装共享数据类
    static class RunnableCusToDec implements Runnable{
        //封装共享数据
        private ShareData shareData;
        public RunnableCusToDec(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.dec();
            }
        }
    }

    /**
     *共享数据类
     **/
    static class ShareData{
        private int num = 10 ;
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void dec() {
            System.out.println("desc方法......");
        }
    }
}

 

package com.hy.多线程高并发;

/** 
* @Description:
 * 2. 多个线程行为不一致共同操作一个数据
 * 如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对
 * 象之间的数据共享:
 * 1) 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享
 * 数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
* @Param:  
* @return:  
* @Author: Hy
* @Date: 2019
*/ 
public class Test6 {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            if(i%2 == 0){
                new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
            }else{
                new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();
            }
        }
    }

    //封装共享数据类
    static class RunnableCusToInc implements Runnable{
        //封装共享数据
        private ShareData shareData;
        public RunnableCusToInc(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }

    //封装共享数据类
    static class RunnableCusToDec implements Runnable{
        //封装共享数据
        private ShareData shareData;
        public RunnableCusToDec(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.dec();
            }
        }
    }

    /**
     *共享数据类
     **/
    static class ShareData{
        private int num = 10 ;
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void dec() {
            System.out.println("desc方法......");
            num--;
            System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

 

2) 将这些 Runnable 对象作为某一个类中的内部类

 

共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类

 
 
以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable 对象调用外部类的这些方法
 
 
 
 
package com.hy.多线程高并发;


/**
* @Description:
 * 2) 将这些 Runnable 对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对
 * 共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个
 * Runnable 对象调用外部类的这些方法
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/

public class Test7 {

    public static void main(String[] args) {
//公共数据
        final ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            if(i%2 == 0){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 5; i++) {
                            shareData.inc();
                        } }
                },"Thread "+ i).start();
            }else{
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 5; i++) {
                            shareData.dec();
                        } }
                },"Thread "+ i).start();
            }
        }
    }

    //
    static class ShareData{
        private int num = 10 ;

        //递增
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //递减
        public synchronized void dec() {
            num--;
            System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值