传统线程的创建方式
传统线程技术回顾
public class TraditionalThread { /** * @param args */ public static void main(String[] args) { //第一种:new Thread() Thread thread = new Thread(){ @Override public void run() { while(true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("1:" + Thread.currentThread().getName()); System.out.println("2:" + this.getName()); } } }; thread.start(); Thread thread2 = new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("1:" + Thread.currentThread().getName()); } } }); thread2.start(); //第二种:new Runnable() new Thread( new Runnable(){ public void run() { while(true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("runnable :" + Thread.currentThread().getName()); } } } ){ public void run() { while(true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread :" + Thread.currentThread().getName()); } } }.start(); } }
传统定时器技术回顾
第一次2秒后输出"bombing" 第二次4秒后输出“bombing”,第三次2秒后输出"bombing" 第四次4秒后输出“bombing”
import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TraditionalTimerTest { private static int count = 0; public static void main(String[] args) { /* new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println("bombing!"); } }, 10000,3000);*/ class MyTimerTask extends TimerTask{ @Override public void run() { count = (count+1)%2; System.out.println("bombing!"); new Timer().schedule(/*new TimerTask() { @Override public void run() { System.out.println("bombing!"); } }*/new MyTimerTask(),2000+2000*count); } } new Timer().schedule(new MyTimerTask(), 2000); while(true){ System.out.println(new Date().getSeconds()); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
传统线程互斥技术
public class TraditionalThreadSynchronized { /** * @param args */ public static void main(String[] args) { new TraditionalThreadSynchronized().init(); } private void init(){ final Outputer outputer = new Outputer(); new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output("zhangxiaoxiang"); } } }).start(); new Thread(new Runnable(){ @Override public void run() { while(true){ try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } outputer.output3("lihuoming"); } } }).start(); } static class Outputer{ public void output(String name){ int len = name.length();
//synchronized 必须使用同一个对象 synchronized (Outputer.class) { for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); } } public synchronized void output2(String name){ int len = name.length(); for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); } public static synchronized void output3(String name){ int len = name.length(); for(int i=0;i<len;i++){ System.out.print(name.charAt(i)); } System.out.println(); } } }
传统线程同步通信技术
例子:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次
import java.util.concurrent.atomic.AtomicInteger; public class TraditionalThreadCommunication { /** * @param args */ public static void main(String[] args) { final Business business = new Business(); new Thread( new Runnable() { @Override public void run() { for(int i=1;i<=50;i++){ business.sub(i); } } } ).start(); for(int i=1;i<=50;i++){ business.main(i); } } } class Business { private boolean bShouldSub = true; public synchronized void sub(int i){ while(!bShouldSub){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j=1;j<=10;j++){ System.out.println("sub thread sequence of " + j + ",loop of " + i); } bShouldSub = false; this.notify(); } public synchronized void main(int i){ while(bShouldSub){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for(int j=1;j<=100;j++){ System.out.println("main thread sequence of " + j + ",loop of " + i); } bShouldSub = true; this.notify(); } }
线程范围内共享变量的概念与作用
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。
这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用,比如说DAO的数据库连接,我们知道DAO是单例的,那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他,并且各自使用各自的。这种情况,ThreadLocal就比较好的解决了这个问题。
我们从源码的角度来分析这个问题。
首先定义一个ThreadLocal:
public class ConnectionUtil { private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); private static Connection initConn = null; static { try { initConn = DriverManager.getConnection("url, name and password"); } catch (SQLException e) { e.printStackTrace(); } } public Connection getConn() { Connection c = tl.get(); if(null == c) tl.set(initConn); return tl.get(); } }
这样子,都是用同一个连接,但是每个连接都是新的,是同一个连接的副本。
那么实现机制是如何的呢?
1、每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。
1
2
3
|
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null ; |
2、当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。
1
2
3
4
5
6
7
8
9
10
|
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null ) { ThreadLocalMap.Entry e = map.getEntry( this ); if (e != null ) return (T)e.value; } return setInitialValue(); } |
3、当我们调用set()方法的时候,很常规,就是将值设置进ThreadLocal中。
4、总结:当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。
5、应用场景:当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal。
6、其实说再多也不如看一下源码来得清晰。如果要看源码,其中涉及到一个WeakReference和一个Map,这两个地方需要了解下,这两个东西分别是a.Java的弱引用,也就是GC的时候会销毁该引用所包裹(引用)的对象,这个threadLocal作为key可能被销毁,但是只要我们定义成他的类不卸载,tl这个强引用就始终引用着这个ThreadLocal的,永远不会被gc掉。b.和HashMap差不多。
事实上,从本质来讲,就是每个线程都维护了一个map,而这个map的key就是threadLocal,而值就是我们set的那个值,每次线程在get的时候,都从自己的变量中取值,既然从自己的变量中取值,那肯定就不存在线程安全问题,总体来讲,ThreadLocal这个变量的状态根本没有发生变化,他仅仅是充当一个key的角色,另外提供给每一个线程一个初始值。如果允许的话,我们自己就能实现一个这样的功能,只不过恰好JDK就已经帮我们做了这个事情。
ThreadLocal类及应用技巧
多个线程之间共享数据的方式探讨
java5原子性操作类的应用
java5线程并发库的应用
Callable与Future的应用
java5的线程锁技术
java5读写锁技术的妙用
java5条件阻塞Condition的应用
java5的Semaphere同步工具
java5的CyclicBarrier同步工具
java5的CountDownLatch同步工具
java5的Exchanger同步工具
java5阻塞队列的应用
java5同步集合类的应用
空中网挑选实习生的面试题1
空中网挑选实习生的面试题2
空中网挑选实习生的面试题3