1.认识线程:
线程有2种启动方式,一种是实现Runnable接口,另一种是继承Thread类。
public class JavaCurrent { public static void main(String[] args) { Thread thread = new Thread(new InnerThread()); thread.start(); } } class InnerThread implements Runnable{ @Override public void run() { //do something } } -----------------------------------------第二种------------------------------------------- public class JavaCurrent { public static void main(String[] args) { InnerThread iThread = new InnerThread(); iThread.start(); } } class InnerThread extends Thread{ @Override public void run() { //do something } }
2. 线程有自己的属性:
每个线程都有自己的属性,这些属性帮助我们更好地区分它们、观察它们的状态等。
线程有id,name,Priority(优先级),status(状态)。
id和name顾名思义,标示thread作用;优先级分为0-10,级数越高说明优先级越高;状态比较重要,有以下几个:
new(初始化), runnable(运行), blocked(堵塞), waiting(等待),terminated(死亡). 具体详情以后解释。
3.去打断一个线程:
线程Thread都有检验自身是否被打断的方法机制,通过isInterrupted()方法来检测当前运行线程是否被打断了。可以通过thread.interrupt()来打断线程,但是,神奇的是当一个线程在运行的时候,调用这个方法完全没有效果 ⊙﹏⊙b汗,线程依旧欢快地运行着。。。
public class JavaCurrent { public static void main(String[] args) { Thread thread = new Thread(new BeInterruptThread()); thread.start(); try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } thread.interrupt(); } } class BeInterruptThread extends Thread{ @Override public void run() { while(true){ if(isInterrupted()){ System.out.println("thread has been Interrupted"); break; }else{ System.out.println("always running"); } } } }
永远都不会打印出“thread has been Interrupted”,如何是好。。。
后来发现,问题出在isInterrupted()方法上,估计是直接调用父类中的isInterrupted()方法,只是获取父类的interrupt属性值,而不是BeInterruptThread对象中的interrupt属性,此处应该改为
@Override public void run() { while(true){ if(Thread.currentThread().isInterrupted()){ System.out.println("thread has been Interrupted"); break; }else{ System.out.println("always running"); } } }
有些时候我们需要在线程遇到打断,或者阻塞时进行线程的退出,这个时候需要使用抛出InterruptedException来实现。
4.使当前运行的线程暂定一下
有时候,想让线程执行一定时期让他暂停,过一段时间之后再让它重新跑起来,在一些周期性的操作业务中经常出现
,我们可以使用sleep()方法。
public class JavaCurrent { public static void main(String[] args) { Thread thread = new Thread(new SleepThread()); thread.start(); } } class SleepThread implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { try { TimeUnit.SECONDS.sleep(1);//暂定一秒哦 } catch (InterruptedException e) { System.out.println("mission failed"); break; } System.out.println("i am counting:" + i); } } }
另外线程在sleep的时候,遇到interrupt的话,会直接抛出InterruptedException,并不会等待sleep结束。
5.让主线程等待
这个想法其实就是让异步的线程变成同步线程,很神奇哈哈。当我们新开一个线程的时候,让执行线程(主线程)等待新开线程执行完毕之后,在继续下去。一般用在一些初始化的地方。
可以使用Thread类中的join()方法
public class JavaCurrent { public static void main(String[] args) { Thread thread1 = new Thread(new JoinThread1(),"first"); Thread thread2 = new Thread(new JoinThread2(),"second"); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("运行结束"); } } class JoinThread1 implements Runnable{ @Override public void run() { System.out.println("begin "+Thread.currentThread().getName()+" run()"); try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end "+Thread.currentThread().getName()+" run()"); } } class JoinThread2 implements Runnable{ @Override public void run() { System.out.println("begin "+Thread.currentThread().getName()+" run()"); try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end "+Thread.currentThread().getName()+" run()"); } }
6.让线程抛出异常
我们知道在run()方法中没法去抛出捕捉的异常,可以换一种方式。使用UncaughtExceptionHandler。
public class JavaCurrent { public static void main(String[] args) { Task task = new Task(); Thread thread = new Thread(task); thread.setUncaughtExceptionHandler(new ThreadUnCaughtExceptionHandler()); thread.start(); } } class Task implements Runnable{ @Override public void run() { int isnum = Integer.parseInt("TTT"); } } public class ThreadUnCaughtExceptionHandler implements UncaughtExceptionHandler{ @Override public void uncaughtException(Thread t, Throwable e) { System.out.printf("Thread: %s\n",t.getId()); e.printStackTrace(); } }
7.本地线程变量
当多个线程同时去执行一个任务对象的时候,他们都在同时使用任务对象中的属性,这是很多情况所不允许的。可以使用一个简单的方法 thread_local 。把需要进行本地线程化的变量声明为:
static ThreadLocal<T> tl= new ThreadLocal<T>() 即可。这样每个线程都有自己的属性,相互之间不受干扰。
8.线程组管理
统一管理一组线程的情况很有可能发生,比如几个线程都去找某个目标,其中一个找到目标的话,就回馈信息,并且退出所有其他线程。这里给出一个ThreadGroup的例子。
public class JavaCurrent { public static int num = 1345; public static void main(String[] args) { NumGenerate ng = new NumGenerate(1000); int[][] array = ng.getDoubleArrray(); //建立线程组 ThreadGroup threadGroup = new ThreadGroup("Search"); for (int i = 0; i < 5; i++) {//开5个线程 SearchThread st = new SearchThread(threadGroup,i+"", array); st.start(); } waitFinish(threadGroup); } private static void waitFinish(ThreadGroup threadGroup) { while(true){ if(threadGroup.activeCount() < 5){//如果正在运行的线程数少于5个了,标示其中一个线程已经运行完毕 threadGroup.interrupt(); break; } //检验线程的状态,如果5个线程全部都是休眠的话,也进行中断退出 Thread[] threads = new Thread[threadGroup.activeCount()]; threadGroup.enumerate(threads); int isSleepCount = 0; for (int i = 0; i < threads.length; i++) { if(threads[i].getState() == State.TIMED_WAITING){ isSleepCount++; } } if(isSleepCount == 5){//说明5个线程都在休眠 threadGroup.interrupt(); System.out.println("all threads are not found !"); break; } } } }
public class SearchThread extends Thread{ private String name;//标记一下线程的名字 private int[][] targetArea;//目标数组 public SearchThread(ThreadGroup tg ,String name,int[][] targetArea) { super(tg, name); this.name = name; this.targetArea = targetArea; } @Override public void run() { int i = Integer.parseInt(this.name);//确定该线程去跑哪行数组 for (int j = 0; j < this.targetArea[i].length; j++) { if(this.targetArea[i][j] == JavaCurrent.num){//说明找到数字了 System.out.println("thread"+this.name+": find the number!-->" + this.targetArea[i][j]+"==============================="); return; } } try { TimeUnit.MINUTES.sleep(MIN_PRIORITY);//进入休眠1分钟 } catch (InterruptedException e) { System.out.println("thread" +this.name+ ":quit!"); } } }
/** * 不重复的随机生成数 * 如果传入的size是1000,那么就生成0-2000范围内的数字,两倍大小 * @author dlsm-syq * */ public class NumGenerate { private int size;//需要生成随机数的数量 private Random random = new Random(); private Set<Integer> sets = new HashSet<Integer>(); public NumGenerate(int size) { this.size = size; generate(); } private void generate() { while(sets.size() != size){//只要sets容器还没有充满,就一直生成 int i = random.nextInt(this.size * 2); if(!sets.contains(i)){ sets.add(i); } } } //获取二维数组,只有5行,因为只开了5个线程 public int[][] getDoubleArrray(){ int col = this.size / 5;//每行有多少列 int[][] array = new int[5][col]; for (Iterator<Integer> iterator = sets.iterator(); iterator.hasNext();) { for (int i = 0; i < 5; i++) { for (int j = 0; j < col; j++) { array[i][j] = iterator.next(); } } } return array; } }
开5个线程,去搜索一个二维数组中的某个数字,只要其中一个线程找到了,就中断所有线程。如果线程没有找到该数字,则进行长时间休眠,如果5个线程都没有找到该数字,则中断所有线程。
9.线程工厂类
使用工厂模式来生产线程,方便线程的管理。主要用法是实现ThreadFactory接口,并且复写public Thread newThread(Runnable r)方法即可,非常简单。
public class MyThreadFactory implements ThreadFactory{ private String name; private int count = 0; public MyThreadFactory(String name) { this.name = name; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r,this.name + "prudct:" + count); this.count ++; return thread; } }
public class JavaCurrent { public static void main(String[] args) { MyThreadFactory mtf = new MyThreadFactory("factory"); for (int i = 0; i < 4; i++) { Thread t = mtf.newThread(new Task()); t.start(); } } } class Task implements Runnable{ @Override public void run() { System.out.println(this.toString()); } }
以上文章均是自己对书本中知识的理解,练手小例子,可能有许多错误的地方,还希望大家多多包含和指导,谢谢。