建立:继承Thread类,重写里面的Run方法java
启动:建立子类对象,调用start方法缓存
public class StartThread extends java.lang.Thread { @Override public void run() { for (int i = 0; i <10 ; i++) { System.out.println("一边吃饭"); } } public static void main(String[] args) { StartThread thread=new StartThread(); thread.start();//开启一个新的线程 下面的代码不受这句代码的影响不须要等待执行完成 继续往下走 for (int i = 0; i <10 ; i++) { System.out.println("一边code"); } } }
运行结果安全
首先进入Main方法,而后调用子类对象的Start方法,会启动run方法,此时不须要等待run方法执行完毕,直接向继续执行”一边code“,start方法至关于开启了一个新的线程,start方法但不保证当即运行。服务器
若是把调用子类的方法改为run,就变成了普通方法,须要等待执行完成再进入下一步多线程
public class StartThread extends java.lang.Thread { @Override public void run() { for (int i = 0; i <10 ; i++) { System.out.println("一边吃饭"); } } public static void main(String[] args) { StartThread thread=new StartThread(); thread.run();//开启一个新的线程 下面的代码不受这句代码的影响不须要等待执行完成 继续往下走 for (int i = 0; i <10 ; i++) { System.out.println("一边code"); } } }
运行结果,先吃饭后code架构
不建议使用,继承了一个类,就不能继承其余父类了并发
建立:实现Runnable接口 重写Run方法ide
启动:建立实现类对象、Thread对象 调用Start方法性能
public class RunnableThread implements Runnable{ @Override public void run() { for (int i = 0; i <20 ; i++) { System.out.println("一边吃饭"); } } public static void main(String[] args) { RunnableThread runnableThread=new RunnableThread(); Thread thread=new Thread(runnableThread); thread.start(); for (int i = 0; i <20 ; i++) { System.out.println("一边打游戏"); } } }
推荐:避免单继承的局限性,优先使用接口优化
创建三个用户模仿抢票
public class RabbitClass extends RunnableThread { private int num=99; @Override public void run() { while (true) { if(num<0) { break; } try { Thread.sleep(200);//模拟延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+num--); } } public static void main(String[] args) { RabbitClass rabbitClass=new RabbitClass(); new Thread(rabbitClass,"one").start();//用户一 new Thread(rabbitClass,"two").start();//用户二 new Thread(rabbitClass,"three").start();//用户三 } }
import java.util.concurrent.*; public class Excallable implements Callable<Boolean> { private int num=99; @Override public Boolean call() throws Exception {//模拟抢票 while (true) { if(num<0) { break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"--->"+num--); } return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { Excallable ccallable=new Excallable(); //建立执行服务 ExecutorService service= Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> retult=service.submit(ccallable); Future<Boolean> retult2=service.submit(ccallable); Future<Boolean> retult3=service.submit(ccallable); //获取结果 boolean r=retult.get(); boolean r2=retult2.get(); boolean r3=retult3.get(); //关闭服务 service.shutdownNow(); } }
Thread.currentThread()
能够得到当前线程,同一段代码可能被不一样的线程执行,所以当前线程是相对的,Thread.currentThread()返回的是代码实际运行时候的线程对象。示例以下
public class SubThread extends Thread { public SubThread() { System.out.println("main里面调用的线程"+Thread.currentThread().getName()); } @Override public void run() { System.out.println("run里面调用的线程"+Thread.currentThread().getName()); } public static void main(String[] args) { System.out.println("main里面调用的线程"+Thread.currentThread().getName()); SubThread subThread=new SubThread(); subThread.start();//子线程 } }
在main方法里面。调用线程因此是main线程,main里面调用构造方法,因此构造方法也是调用main线程,当启动子线程至关于开启了一个新的线程。
setName能够设置线程名称,getName能够获取线程名称,经过设置线程名称有助于程序调试,提升可读性,建议为每个线程设置一个能够体现线程功能的名称。
isAlive能够判断线程是否处于活动状态,
public class SubThread extends Thread { @Override public void run() { System.out.println("run方法-->"+isAlive()); } public static void main(String[] args) {java SubThread subThread=new SubThread(); System.out.println("begin-->"+subThread.isAlive()); subThread.start(); System.out.println("end-->"+subThread.isAlive());//此时线程结束有可能返回false,不定性 } }
sleep方法让当前线程休眠指定毫秒数
Thread.getId()能够得到线程的惟一标识
某个编号的线程运行结束以后可能又被其余线程使用,重启JVM以后,同一个线程的id可能不同。
Thread.yieId()方法做用是放弃当前的CPU资源
Thread.setpropority(num)设置线程优先级
java线程优先级取值范围:1~10,超过这个范围会异常
操做系统中,优先级较高的线程得到CPU的资源比较多
线程的优先级本质上是给线程调度器一个提示,用于决定先调度那些线程,并不能保证线程先运行
优先级若是设置不当可能致使某些线程永远没法运行,即产生了线程饥饿。
线程优先级并非设置的越高越好,通常设置普通优先级就好。线程的优先级具备继承性,在A线程中建立B线程,则B线程的优先级与A线程同样。
中断线程,该方法仅仅是在当前线程打一个中止标志,并非真正的中止线程
public class SubThread extends Thread { @Override public void run() { for (int i = 0; i <10000 ; i++) { System.out.println("run-->"+i); if(this.isInterrupted()) { System.out.println("线程中断退出"); return;//直接结束run方法 } } } public static void main(String[] args) { SubThread subThread=new SubThread(); subThread.start();//子线程 for (int i = 0; i <100 ; i++) { System.out.println("main-->"+i); } subThread.interrupt();//标记线程中断此时isInterrupted=true 线程并无中断 } }
java 中线程分为用户线程和守护线程
守护线程是为其余线程提供服务的线程,如垃圾回收器(GC)就是一个守护线程
守护线程不能单独运行,当JVM中没有其余用户线程,只有守护线程,守护线程会自动销毁。
public class SubThread extends Thread { @Override public void run() { for (int i = 0; i <10000 ; i++) { System.out.println("run-->"+i); } } public static void main(String[] args) { SubThread subThread=new SubThread(); subThread.setDaemon(true);//设置线程守护 subThread.start();//子线程 for (int i = 0; i <100 ; i++) { System.out.println("main-->"+i); } } }
设置线程守护之后,子线程run运行了一段才中止,由于设置线程守护之后销毁须要时间。
线程的生命周期能够经过getstate()得到,Thread.state类型分为
New:新建状态,建立了线程对象,在Start启动前的状态
Runnable可运行状态:包含READY
,表示该线程能够被资源调度器进行调度。使它处于RUNNING
状态,RUNNING
状态表示该线程正在执行,若是用yieid方法能够把RUNNING
状态转化为READY
状态
Waiting等待状态:线程执行了wait()、thread.join 方法会把线程转化为Waiting
等待状态,执行object.notify()方法,或者加入的线程执行完毕,当前线程会转化为RUNNABLE
状态。
TimeD_WAITING状态:跟Waiting相似,可是若是没有在指定范围实际完成指望操做,会自动转化为RUNNABLE状态。
TERMINARED状态:,终止,线程结束
线程安全问题,多线程共享数据时,若是没有采起正确的并发控制措施,就可能产生数据一致性的问题,如读取过时的数据,丢失数据更新。
线程活性问题,因为程序自身的缺陷致使哦线程一直处于非RUNNABLE状态,常见的活性故障有:
上下文切换(Context Switch)问题,处理器从一个线程切换到另外一个线程
可靠性问题,可能会由一个线程致使JVM意外终止,其余线程没法执行
非线程安全就是指多个线程对同一个实例对象进行操做的时候有只被更改或者值不一样步的问题。
线程安全问题表如今三个方面:
原子就是不可分割的意思,有两层含义:
(1)访问共享变量的操做,其余线程来看,要么已经关闭,要么执行完成,其余线程看不到这个操做的中间结果
(2)访问同一种共享变量的原子操做是不能交错的
用户ATM取钱,要么成功取到钱了余额发生变动,要么失败什么都没有变
java有两种方法实现原子性:
(1)使用锁(锁具备排它性,一时刻只能被一个线程访问)
(2)使用处理器的CAS指令(硬件锁)
在多线程中,一个线程对某个共享变量进行修改,其余线程可能不能当即获取到这个更新的结果
若是更新以后能获取到则这个线程具备可见性,不然不具备可见性。可能会致使其余线程读取到脏数据。
有序性是指在某些状况,下一个处理器上运行的一个线程所执行的内存访问操做在另外一个处理器的其余线程看来是乱序的。
在多核处理器的环境下,编写代码的顺序可能不会是执行的顺序,在一个处理器上执行的顺序,在其余处理器上看起来和代码不同,这种现象称为重排序。重排序是对内存访问操做的优化,前提是单线程,可是对多线程的正确性能可能会有影响。
操做顺序概念
能够把重排序分为指令重排序和存储子系统重排序:
指令重排序主要有JIT
编译器处理器引发的,指程序顺序和执行顺序不同
指令重排序是一种动做,确实对指令的顺序作了调整,Javac编译器通常不会执行指令重排序,而JIT编译器可能执行。CPU处理器可能执行指令重排序,使得执行顺序与程序顺序不一致。
存储子系统重排序是由高速缓存,写缓冲器引发的,感知顺序和执行顺序不一致。
高速缓存是cpu为了匹配与主内存处理速度不匹配而设计的高速缓存,写缓存器用来提升写高速缓存的效率,即便处理器严格执行两个内存的访问操做,在存储子系统的做用下其余处理器对操做的操做顺序和感知顺序可能不一致。
存储子系统排序并无对指令顺序进行排序,而是形成指令执行顺序被调整的假象。存储子系统重排序对象是内存操做的结果。
从处理器角度来看, 读内存就是从指定的 RAM 地址中加载数据到 寄存器,称为 Load 操做; 写内存就是把数据存储到指定的地址表示 的 RAM 存储单元中,称为 Store 操做.
内存重排序有如下四种可能:
内存重排序与具体的处理器微架构有关,不一样架构的处理器所容许的内存重序不一样
JIT 编译器,处理器,存储子系统是按照必定的规则对指令,内存操做的结果进行重排序, 给单线程程序形成一种假象----指令是按照源码 的顺序执行的.这种假象称为貌似串行语义. 并不能保证多线程环境 程序的正确性
为了保证貌似串行语义,有数据依赖关系的语句不会被重排序,只 有不存在数据依赖关系的语句才会被重排序.若是两个操做(指令)访 问同一个变量,且其中一个操做(指令)为写操做,那么这两个操做之间 就存在数据依赖关系(Data dependency).
x = 1; y = x + 1; 后一条语句的操做数包含前一条语句的执行结果
若是不存在数据依赖关系则可能重排序,如:
double price = 45.8; int quantity = 10; double sum = price * quantity;
可使用 volatile 关键字, synchronized 关键字实现有序性