关于什么是线程,以及线程和进程之间的区别可以看之前的一篇文章《Linux下的TCP/IP编程—线程及多线程服务端》
线程是有优先级的。但是这种优先级并不可靠,主要取决于其JVM是如何进行实现的,所以说依赖于线程优先级的编程是不可靠的,程序的正确性不能依赖于线程的优先级,可以通过线程的priority属性来设置线程的优先级。
线程的状态(声明周期):
NEW:初始状态,线程被构建,但是还没有调用start方法开始运行RUNNABLE:运行状态,Java线程将操作系统中的就绪和运行两种状态统称为运行BLOCKED:阻塞状态,表示线程阻塞于锁WAITING:等待状态,表示进入该状态的线程需要等待其他一些线程做出一些动作才能继续运行TIME_WAITING:超时等待状态,在等待状态的基础上加入了超时机制,在指定的时间自行返回TERMINATED: 终止状态,表示当前线程已经执行完毕Daemon线程:这是一种支撑型线程,主要用于程序中后台调度以及支持性工作,主要特性是:当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出,而不考虑Daemon线程所处的状态,所以Daemon线程中的finally块并不可靠,有可能无法被执行到。可以通过线程的Thread.setDaemon(true)方法将线程设置为Daemon线程。
线程的构造:实现线程主要有三种方式,1>是继承自Thread类;2>是实现Runnable接口,将接口的实现类传入Thread中;3>是使用Callable和Future代码示例:
/** * 继承Thread类,并重写其run方法 * @author WQC * */ class MyThread extends Thread{ @Override public void run() { // TODO Auto-generated method stub super.run(); } } MyThread myThread = new MyThread(); MyThread.start(); /** * 实现Runnable接口,重写run方法,并在创建线程时传入Runnable的实例 * @author WQC * */ class MyRunnable implements Runnable{ @Override public void run() { // TODO Auto-generated method stub } } MyRunnable myRunnable = new MyRunnable(); Thread runnableThread = new Thread(myRunnable); runnableThread.start(); /** * 实现Callable接口并重写call()方法,并在使用时用FutureTask来包装Callable对象,在创建线程时将FutureTask传入Thread中 * @author WQC * */ class MyCallable implements Callable<Integer>{ @Override public Integer call() throws Exception { // TODO Auto-generated method stub return null; } } MyCallable myCallable = new MyCallable(); FutureTask<Integer> futureTask = new FutureTask<Integer>(myCallable); Thread callThread = new Thread(futureTask); callThread.start(); 线程的销毁:线程中可以有stop()方法来停止线程,但是这种方式已不推荐使用,因为不能保证线程的所占用的资源正常释放,通常未完成资源释放变已经停止了运行。最合适的方式是在线程中套用一个循环,通过循环的标志位来确定是否可以跳出循环,停止线程执行。 @Override public void run() { //通过标志位来结束线程,可以做一些资源回收操作 while(isStop){ //要执行的任务 } }volatile可以用来修饰类中的字段,告知程序任何对该变量的访问都需要从共享内存中获取,而对他的修改必须立即同步刷新回共享内存中,以此来保证多线程访问时该字段的可见性和一致性
public volatile boolean isStop = false;synchronized可以用来修饰方法或者是代码块,主要是保证多线程在同一时刻只能有一个线程处于synchronized方法或者是synchronized代码块中,保证了线程对变量访问的可见性和排他性。
/** * 同步方法 * @param data */ public synchronized void setData(int data){ //需要同步的方法 } /** * 同步代码块 * @return */ public boolean isEmpty(){ synchronized (this) { //需要进行同步的代码 } return emptyFlag }主要工作原理是当一个线程A的执行时发现线程B还有一些工作尚未完成,此时可以在线程A中调用wait()方法来使线程A进入WAITING状态,此时线程A会释放所占用的资源。直到线程B完成了工作之后调用notify()或者是notifyAll()方法来通知线程A从WAITING状态转变为RUNABLE状态。
wait():调用改方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,调用wait()之后线程会释放资源wait(long): 在wait()的基础上加入超时等待机制,时间单位是毫秒wait(long,int):时间粒度可以控制到纳秒notify():通知一个在对象上等待的线程,使其从wait()方法返回,前提是该线程获取到了对象的锁notifyAll():通知所有等待在该对象上的线程通过使用内存作为媒介来进行线程间的数据传输工作,主要有四种具体实现:PipedOutputStream,PipedInputStream(面向字节流),PipedReader,PipedWriter(面向字符流)
//声明输入输出的管道 PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); //将管道连接起来 out.connect(in); //输出线程开始输出 Thread printer = new Thread(new Print(in),"Printer"); printer.start(); //主线程作为接收线程开始接收 int recive = 0; while((recive = System.in.read()) != -1){ out.write(recive); } //关闭管道 out.close(); ------- //输出线程 static class Print implements Runnable{ private PipedReader in; public Print(PipedReader reader) { // TODO Auto-generated constructor stub this.in = reader; } @Override public void run() { // TODO Auto-generated method stub int revive = 0; try { while((revive = in.read())!=-1){ System.out.println((char)revive); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待线程thread终止之后才从thread.join()返回也就是说通过Thread.join()方法可以实现线程之间的先后顺序,使得一个线程执行完一些准备工作之后另一个线程开始工作。
ThreadLocal即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值,可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法渠道原先设置的值。
public class ThreadLocalDemo { public static void main(String[] args) throws Exception { ThreadLocalDemo.begin(); TimeUnit.SECONDS.sleep(1); System.out.println(ThreadLocalDemo.end()+""); } private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>(){ protected Long initialValue() { return System.currentTimeMillis(); }; }; public static final void begin(){ TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static final long end(){ return System.currentTimeMillis()-TIME_THREADLOCAL.get(); } }向线程池提交任务有两种方式,一种是通过execute()方法,一种是通过submit()方法。
execute():这种方式没有返回值,所以无法判断任务是否执行完毕
submit():这种方式会返回一个Future对象,可以通过Future对象的get()方法来获取返回值,但是调用get()方法会阻塞当前线程,直到任务完成。
//通过execute提交任务 threadPoolExecutor.execute(command); //通过submit提交任务 threadPoolExecutor.submit(command);关闭线程池可以使用shutdown()方法,或者是shutdownNow()方法,两者的区别在于:shutdownNow方法是先将线程池的状态设为stop,然后尝试停止所有的线程,并返回正在执行任务的线程列表;shutdown则是将线程池的状态设置为stop,然后将停止所有未执行任务的线程。
//两中停止方式 threadPoolExecutor.shutdown(); threadPoolExecutor.shutdownNow();