package com.itcast.family;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* 案例说明:线程范围内的共享变量以及应用场景(转账,转入和转出);在线程内共享,在线程外独立
*/
public class ThreadScopeShareData {
//使不同模块拿到相同线程的相同数据
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 void get() {
int data = threadData.get(Thread.currentThread());
System.out.println("A from " + Thread.currentThread().getName()
+ " get data: " + data);
}
}
static class B {
public void get() {
int data = threadData.get(Thread.currentThread());
System.out.println("B from " + Thread.currentThread().getName()
+ " get data: " + data);
}
}
}
3. 使用ThreadLocal达到与上个案例相同的需求
package com.itcast.family;
import java.util.Random;
/**
* 使用ThreadLocal达到与上个案例相同的需求
*/
public class ThreadLocalTest {
//使不同模块拿到相同线程的相同数据
//直接使用ThreadLocal传入数据
private static ThreadLocal<Integer> threadData = new ThreadLocal<Integer>();
//使用ThreadLocal传入对象中封装的数据;普通思路用到的变量
//private static ThreadLocal<MyThreadScopeData> myData = new ThreadLocal<MyThreadScopeData>();
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.set(data);
//一般思路,符合基本的面向对象编程,但是用着啰嗦臃肿,代码不够优化
/* MyThreadScopeData myThread = new MyThreadScopeData();
myThread.setName("get nameData: "+data);
myThread.setAge(data);
myData.set(myThread);*/
//优雅和优化的思路,类似单例模式封装代码,因为使用者只需知道怎么用干什么用的,而不需知道内部代码是什么
MyThreadScopeData.getThreadInstance().setName(" get nameData: "+data);
MyThreadScopeData.getThreadInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
static class A {
public void get() {
int data = threadData.get();
System.out.println("A from " + Thread.currentThread().getName()
+ " get data: " + data);
//一般思路,符合基本的面向对象编程,但是用着啰嗦臃肿,代码不够优化
/* MyThreadScopeData myThread = myData.get();
System.out.println("A from " + Thread.currentThread().getName()
+ myThread.getName()+" ;get ageData: "+myThread.getAge());*/
//优雅和优化的思路,类似单例模式封装代码,因为使用者只需知道怎么用干什么用的,而不需知道内部代码是什么
MyThreadScopeData myThread = MyThreadScopeData.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName()
+ myThread.getName()+" ; get ageData: "+myThread.getAge());
}
}
static class B {
public void get() {
int data = threadData.get();
System.out.println("B from " + Thread.currentThread().getName()
+ " get data: " + data);
/* MyThreadScopeData myThread = myData.get();
System.out.println("B from " + Thread.currentThread().getName()
+ myThread.getName()+" ;get ageData: "+myThread.getAge());*/
MyThreadScopeData myThread = MyThreadScopeData.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName()
+ myThread.getName()+" ; get ageData: "+myThread.getAge());
}
}
}
class MyThreadScopeData{
//可以参考单例模式来编写更加优化和优雅的线程代码
private MyThreadScopeData(){}
//线程这里与单例有些许不同,因为只有一个对象,所以可以不用使用同步锁
public static/*synchronized*/MyThreadScopeData getThreadInstance(){
MyThreadScopeData instance = map.get();
if(instance == null){
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
/*只是告诉阅读者类似单例代码,但是与单例不同
* private static MyThreadScopeData intance = null;//new MyThreadScopeData();
//饱汉式,不管用不用先new出来;这里用饿汉式,用的时候才new出来
*/
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
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;
}
}
4. 面试题: 设计4个成员,其中两个线程每次对j加1;另两个线程每次对j减1,代码如下分析:
package com.itcast.family;
/**
* 面试题:
* 设计4个成员,其中两个线程每次对j加1;
* 另两个线程每次对j减1,代码如下分析:
*/
public class MultiThreadShareData {
// 面试题答案:
private int j;
public static void main(String[] args) {
MultiThreadShareData data = new MultiThreadShareData();
Inc inc = data.new Inc();
Dec dec = data.new Dec();
//创建四个线程,如题:两个增加两个减少
for (int i = 0; i < 2; i++) {
new Thread(inc).start();
new Thread(dec).start();
}
}
/*
* 不直接把j++或j--写到对应的实现Runnable类的代码中,
* 而是抽出成外部类的两个方法;是因为外部类的方法方便使用
* 同步锁,更容易控制并发问题
*/
private synchronized void inc() {
j++;
System.out.println(Thread.currentThread().getName() + "-inc:" + j);
}
private synchronized void dec() {
j--;
System.out.println(Thread.currentThread().getName() + "-dec:" + j);
}
class Inc implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
inc();
}
}
}
class Dec implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
dec();
}
}
}
/*//第一种方案传入的data变量要么是静态成员变量要么是最终普通变量,否则会出错;因为两个内部类访问可以访问同一个外部类的成员变量
private static ShareData data1 = new ShareData();
public static void main(String[] args) {
final ShareData data1 = new ShareData();
ShareData data2 = new ShareData();
//第一种方案:这两个线程分别实现两个Runnable的run方法,分别用来执行加减
new Thread(new Runnable() {
@Override
public void run() {
data1.increment();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
data1.decrement();
}
}).start();
//第二种方案:传入实现了Runnable接口的类对象,相应的需求方法封装在传入的类中
new Thread(new MyRunnable1(data2)).start();
new Thread(new MyRunnable2(data2)).start();
}
}
//第二种方案
class MyRunnable1 implements Runnable{
private ShareData data;
MyRunnable1(ShareData data) {
super();
this.data = data;
}
public void run() {
data.increment();
}
}
class MyRunnable2 implements Runnable{
private ShareData data;
MyRunnable2(ShareData data) {
super();
this.data = data;
}
public void run() {
data.decrement();
}
}
//因为需求是一个加一个减,即两个不同的线程,仅实现一个Runnable接口是不够的
class ShareData{ implements Runnable{
private int count = 100;
@Override
public void run() {
while(true){
count --;
}
}
private int j = 0;
public void increment(){
j++;
}
public void decrement(){
j--;
}
*/
}