关于Java多线程和并发运行的学习(二)
2014-09-09 16:51
197 查看
这几天生病了,所以学的比较少,只学习了关于线程中的线程范围数据共享,ThreadLocal以及多线程数据共享。
在这里记下我的理解和代码,其实理解都写在代码注释里了,个人觉得这样更加适合以后复习。
(上一篇我讲的是关于线程的创建,计时器,同步锁,线程间的通信。)
运行结果是:
不过上面的例子只是说明线程范围的数据共享。一般在开发时,我们使用ThreadLocal这个东西。
每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。
ThreadLocal的应用场景:
* 1.订单处理包含一系列操作:减少库存量,增加一条流水台帐,修改总账,这几个操作要在同一个事务完成,也就是在同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。
* 2.银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象, 转入和转出操作的代码分别是两个不同的账户对象的方法
* 3.例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中调用getContext方法,拿到的都是同一个。
这面的代码可以清晰表现出ThreadLocal的应用:
MyThreadScopeData.java
ThreadLocalTest.java
其中利用的单例模式的演变形式,可以通过获取实例来得到对象,而不是new一个对象。
* 总结:一个ThreadLocal代表一个变量,故其中只能放一个数据,你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象。如果有一个自变量要线程共享呢?那请先定义一个对象来装这个自变量, 然后在ThreadLocal中存储这个对象。
如果每个线程执行的代码不同,这时候需要用不同的Runnable对象, 有如下两种方式来实现这些Runnable对象之间的数据共享:
* 1.将共享数据封装在A对象中,然后将A对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到各个Runnable对象中去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
* 2.将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量, 每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
* 3.上面两种方式的组合:将共享数据封装在A对象中,每个线程对共享数据的操作方法也分配到A对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量, 每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码分别放在几个独立的方法中,然后把这些方法再放在同一个类中, 这样比较容易实现他们之间的同步互斥和通信。
********* 另外的例子在《面试宝典》的第28题。
下面有一个小例子:
在这里记下我的理解和代码,其实理解都写在代码注释里了,个人觉得这样更加适合以后复习。
(上一篇我讲的是关于线程的创建,计时器,同步锁,线程间的通信。)
第一个,关于线程范围内的数据共享。
数据共享,一般是多个事件在串在同一个线程里调用同一个数据。下面一个小例子就可以说明清楚:package Multithreading; import java.util.HashMap; import java.util.Map; import java.util.Random; public class ThreadScopeShareData { //private static int data = 0; private static Map<Thread,Integer> threadData = new HashMap<Thread,Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data : " + data); threadData.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); } } static class A { public int get() { int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " has put data : " + data); return data; } } static class B { public int get() { int data = threadData.get(Thread.currentThread()); System.out.println("B from " + Thread.currentThread().getName() + " has put data : " + data); return data; } } }
运行结果是:
不过上面的例子只是说明线程范围的数据共享。一般在开发时,我们使用ThreadLocal这个东西。
第二个,关于ThreadLocal:
ThreadLocal实现线程范围的共享变量:用于实现线程内的数据共享, 即对于相同的程序代码,多个模块在同一个线程中运行时要共享的一份数据,而在另外线程中运行时又共享另外一份数据。每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。
ThreadLocal的应用场景:
* 1.订单处理包含一系列操作:减少库存量,增加一条流水台帐,修改总账,这几个操作要在同一个事务完成,也就是在同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。
* 2.银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象, 转入和转出操作的代码分别是两个不同的账户对象的方法
* 3.例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中调用getContext方法,拿到的都是同一个。
这面的代码可以清晰表现出ThreadLocal的应用:
MyThreadScopeData.java
package Multithreading; class MyThreadScopeData { /** * 单例模式的演变 */ private MyThreadScopeData() {}// 防止外部使用new private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); public static/* synchronized */MyThreadScopeData getThreadInstance() { /** * 此处可以不加synchronized,因为A线程进来,instance如果为null,就会执行new,如果在其未返回值前线程切换, * B线程进来, 获取instance,发现其值为空,紧接着会执行new,但是这个两个不同的线程,操作的是不同的数据, * 其创建的实例属于各线程自己的实例,互不影响,所以这里不需要同步锁。 */ MyThreadScopeData instance = map.get(); if (instance == null) { instance = new MyThreadScopeData(); map.set(instance); } return instance; } private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
ThreadLocalTest.java
package Multithreading; import java.util.Random; public class ThreadLocalTest { public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data : " + data); MyThreadScopeData.getThreadInstance().setName("name:" + data); MyThreadScopeData.getThreadInstance().setAge(data); new A().get(); new B().get(); } }).start(); } } static class A { public void get() { MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); System.out.println("A from "+Thread.currentThread().getName() + " getMyData: " + myData.getName()); System.out.println("A from "+Thread.currentThread().getName() + " getMyData: " + myData.getAge()); } } static class B { public void get() { MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); System.out.println("B from "+Thread.currentThread().getName() + " getMyData: " + myData.getName()); System.out.println("B from "+Thread.currentThread().getName() + " getMyData: " + myData.getAge()); } } }
其中利用的单例模式的演变形式,可以通过获取实例来得到对象,而不是new一个对象。
* 总结:一个ThreadLocal代表一个变量,故其中只能放一个数据,你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象。如果有一个自变量要线程共享呢?那请先定义一个对象来装这个自变量, 然后在ThreadLocal中存储这个对象。
第三个,多线程间的数据共享
如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有共享数据,例如:卖票系统就可以这么做。如果每个线程执行的代码不同,这时候需要用不同的Runnable对象, 有如下两种方式来实现这些Runnable对象之间的数据共享:
* 1.将共享数据封装在A对象中,然后将A对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到各个Runnable对象中去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
* 2.将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量, 每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
* 3.上面两种方式的组合:将共享数据封装在A对象中,每个线程对共享数据的操作方法也分配到A对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量, 每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码分别放在几个独立的方法中,然后把这些方法再放在同一个类中, 这样比较容易实现他们之间的同步互斥和通信。
********* 另外的例子在《面试宝典》的第28题。
下面有一个小例子:
package Multithreading; public class MultiThreadShareData { public static void main(String[] args) { final ShareData1 data1 = new ShareData1(); new Thread(new Runnable() { public void run() { data1.increment(); } }).start(); new Thread(new Runnable() { public void run() { data1.decrement(); } }).start(); } } class ShareData1 { private int j = 0;; public synchronized void increment() { j++; } public synchronized void decrement() { j--; } }
相关文章推荐
- 关于Java多线程和并发运行的学习(五)——同步集合类
- 关于Java多线程和并发运行的学习(四)——之CyclicBarrier
- 关于Java多线程和并发运行的学习(一)
- 关于Java多线程和并发运行的学习(五)——并发 Collection
- 关于Java多线程和并发运行的学习(四)——之Semaphore
- 关于Java多线程和并发运行的学习(四)——之Condition
- 关于Java多线程和并发运行的学习(三)
- 关于Java多线程和并发运行的学习(五)——阻塞队列
- 关于Java多线程和并发运行的学习(四)——之CountDownLatch
- 关于Java多线程和并发运行的学习(四)——之Exchanger
- 关于java并发锁的学习总结
- 关于Android SQLite3多线程并发问题,学习笔记。
- Java多线程编程--(8)学习Java5.0 并发编程包--线程池、Callable & Future 简介
- 【学习anroid编译】关于运行android模拟器
- Java多线程编程--(9)学习Java5.0 并发编程包--线程工具类
- C#学习小记7一个多载,及我想实验关于抽象类能否直接编译运行
- Java多线程和并发编程实践学习总结---提高篇1
- 【JAVA基础】关于JAVA中的static方法、并发问题以及JAVA运行时内存模型
- JAVA多线程与并发学习总结
- 关于JAVA多线程并发synchronized的测试与合理使用