练习:主线程,子线程互相交互:
package cn.itcast.thread;
public class TestSubMainThread {
private static boolean flag = true;
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
business.sub(i);
}
}
}).start();
for (int i = 0; i < 20; i++) {
business.main(i);
}
}
private static void business() {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
synchronized (TestSubMainThread.class) {
try {
if (flag)
TestSubMainThread.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int j = 0; j < 50; j++) {
System.out.println(Thread.currentThread().getName()
+ "sub---" + j + "of " + i);
}
flag = !flag;
TestSubMainThread.class.notify();
}
}
}
}).start();
for (int i = 0; i < 20; i++) {
synchronized (TestSubMainThread.class) {
if (!flag) {
try {
TestSubMainThread.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName()
+ "main---" + j + "of " + i);
}
flag = !flag;
TestSubMainThread.class.notify();
}
}
}
}
package cn.itcast.thread;
public class Business {
private boolean flag = true;
public synchronized void sub(int i) {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 0; j < 50; j++) {
System.out.println(Thread.currentThread().getName() + "sub " + j
+ "loop of " + i);
}
flag = !flag;
this.notify();
}
public synchronized void main(int i) {
while (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName() + "main " + j
+ "loop of " + i);
}
flag = !flag;
this.notify();
}
}
这个代 是自己写的,心得体会:
线程的启动和线程需要执行的代 应该要进行分离,同时如果需要被线程执行的代 要解决安全问题,我们应该在内部解决,也就是当成一个业务来做,用户只要直接使用,即使在多线程情况也不会出现安全问题,这充分体现了面向对象的封装性,也即软件工程思想的高类聚思想;
这个练 主要是要求我们对线程的通信机制有一个比较透彻的了解,等待唤醒机制,注意到什么时候该等待,什么时候该改变 记,什么时候该唤醒对方。
线程范围的数据:ThreadLocal,该类的底层实现原理是,有一个Map集合,用于存储数据,以Thread对象为关键字,以数据为value;
此设置时只要设置数据即可,即threadLocal.set(data);//将数据绑定到线程上;
取出操作threadLocal.get();//取出当前线程绑定的数据;
据这个思路我们想,我们是否可以设计出一个跟当前线程相关的实例对象,在该实例对象中存储数据,取出数据;
这个我们可以从单例设计模式中的懒汉式中得到启发;
判断某个线程上是否存在数据(实例对象);如果有怎返回这个实例对象,我们得到了绑定到某个线程的实例对象,用该对象进行存取数据的操作;如果返回的是空,那么我们就创建一个实例对象,然后绑定到某个线程;当然依 的是ThreadLocal对象;
代 如下:
用面向对象的思想来看,这种设计体现了封装性,用户使用时直接就可以获取到和线程绑定的实例对象;
struts2就是 据这个原理来解决线程安全问题的,他针对每一个请求都创建一个action,将所有和该请求相关的数据和对象封装到一个action中,然后将这个action绑定到当前线程;请求一结束就摧毁;
package cn.itcast.threadlocal;
public class ThreadLocalInstance {
private String name;
private int age;
public ThreadLocalInstance() {
super();
}
private final static ThreadLocal map = new ThreadLocal();
private static ThreadLocalInstance instance = null;
public static ThreadLocalInstance getInstance() {
instance = map.get();
if (instance == null) {
instance = new ThreadLocalInstance();
map.set(instance);
}
return instance;
}
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;
}
}
ActionContext类的设计就是这样滴;
同时我们应该清楚ThreadLocal并不是真正的解决了线程安全问题,只是他给每个线程都copy一个共享数据的副本;
因此类似于单线程;
转发至微博
转发至微博