经过Thread类或Runnable接口建立线程对象以后进入初始状态;调用start方法进入可运行状态(就绪状态),此时并非真正的运行,只是表明已经作好了运行前的各项装备;若是此线程获取到cpu的时间片,则进入到真正的可运行状态,执行run方法里面的业务逻辑;若是run方法执行完毕或调用stop方法则线程运行结束,进入死亡状态;在运行状态时调用不一样方法也会进入其余不一样状态,若是调用强制运行方法join或休眠方法将进入等待状态,时间到后自动进入就绪状态,随时准备获取cpu时间片;若是看到synchronized则进入同步队列等待状态,或者若是调用了wait方法则进入等待状态,等待状态的线程必需要经过notify唤醒才可进入等待状态,若是其它线程执行完毕,本线程拿到同步锁则进入就绪状态,等待获取cpu时间片。某个线程是否会执行只能看它可否争抢到cpu时间片,可是经过调高优先级来让线程更大几率的被优先执行。 参考文档:https://mp.weixin.qq.com/s?src=11×tamp=1513562547&ver=581&signature=30FEkCCQvF3E1tt67vYVym5tRNsSk3d8HGe0v9TAonJmhLh4-53fDEBbgwNFOlgp5rAlGFAJQXYnviaFRwiQ9NmbtIWnZGpotGcuV0Ok*3WzWxg4X6e2mxU0JrgbRb&new=1html
多线程运行的原理是:cpu在线程中作时间片的切换。cpu负责程序的执行,在每一个时间点它其实只能运行一个程序而不是多个程序,不停的在多个程序之间高速切换,而一个程序其实就是一个进程即多个线程,说到底其实就是cpu在多个线程之间不停的作高速切换,而开多个线程就是不让cpu歇着,最大程度的压榨它来为程序服务。实现多线程有三种方式:继承Thread类;实现Runnable接口;使用线程池。java
public class MyExtendsThread extends Thread {
String flag;
public MyExtendsThread(String flag){
this.flag = flag;
}
@Override
public void run(){
String name = Thread.currentThread().getName();
System.out.println("线程"+name+"开始工做了...");
Random random = new Random();
for (int i = 0;i < 20;i++){
try {
Thread.sleep(random.nextInt(10)*100);
System.out.println(name+"============="+flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t0 = new MyExtendsThread("t0");
Thread t1 = new MyExtendsThread("t1");
t0.start();
t1.start();
// t0.run();
// t1.run();
}
}
复制代码
调用线程要用start方法,而不是run方法,使用run方法只是调用方法,实际执行的仍是Main线程,而调用start方法能够明显的看到线程争抢。git
public class MyThreadImplementRunnable implements Runnable {
int x;
public MyThreadImplementRunnable(int x) {
this.x = x;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("线程"+name+"开始执行");
Random random = new Random();
for(int i = 0;i<20;i++){
try {
Thread.sleep(random.nextInt(10)*100);
System.out.println(name+"============="+x);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyThreadImplementRunnable(1),"线程1");
Thread t2 = new Thread(new MyThreadImplementRunnable(2),"线程2");
t1.start();
t2.start();
}
}
复制代码
public class MyThreadImplementCallable implements Callable<String> {
String name;
public MyThreadImplementCallable(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"开始工做==============");
Random random = new Random();
Thread.sleep(random.nextInt(5)*100); //模拟执行业务
return name+":执行完成";
}
public static void main(String[] args) throws Exception{
MyThreadImplementCallable callable = new MyThreadImplementCallable("测试");
FutureTask<String> futureTask = new FutureTask<String>(callable);
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get(); //获取任务线程执行结果
System.out.println("线程的执行结果:"+result);
}
}
复制代码
见下面的线程池专讲。 参考文档:https://www.cnblogs.com/langtianya/archive/2013/03/14/2959713.htmlgithub
public class MySynchronized {
public static void main(String[] args){
final MySynchronized synchronized1 = new MySynchronized();
final MySynchronized synchronized2 = new MySynchronized();
new Thread("thread1"){
@Override
public void run(){
synchronized (synchronized1){
try {
System.out.println(this.getName()+":start");
Thread.sleep(1000);
System.out.println(this.getName()+":wake up");
System.out.println(this.getName()+":end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread("thread2"){
@Override
public void run() {
synchronized (synchronized1){ //争抢同一把锁时,线程1没释放以前,线程2只能等待
// synchronized (synchronized2){ //若是不是一把锁,能够看到两句话交叉打印,发生争抢
System.out.println(this.getName()+":start");
System.out.println(this.getName()+":end");
}
}
}.start();
}
}
复制代码
synchronized是java中的关键字,属于java语言的内置特性。若是一个代码块使用synchronized修饰,则这块代码是同步的,当一个线程获取到这个锁而且开始执行时,其它线程只能一直眼睁睁的等着这个线程执行而后释放锁,其中释放锁只有两种缘由:1.线程正常执行完毕;2.线程执行时发生异常,jvm自动将锁释放。能够看到使用synchronized关键字以后每一个时刻只会有一个线程执行代码块里面的共享代码,线程安全;缺点也很明显,其它线程只能等锁释放,资源浪费严重。编程
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
}
复制代码
lock()、tryLock()、tryLock(long time, TimeUnit unit)、lockInterruptibly()是用来获取锁的。 unLock()方法是用来释放锁的。缓存
public class MyLock {
private static ArrayList<Integer> arrayList = new ArrayList<Integer>();
private static Lock lock = new ReentrantLock();
public static <E> void main(String[] args) {
new Thread() {
@Override
public void run() {
Thread thread = Thread.currentThread();
lock.lock(); //获取锁
try {
System.out.println(thread.getName() + "获得了锁");
for (int i = 0; i < 5; i++) {
arrayList.add(i);
}
} catch (Exception e) {
} finally {
System.out.println(thread.getName() + "释放了锁");
lock.unlock(); //释放锁
}
};
}.start();
new Thread() {
@Override
public void run() {
Thread thread = Thread.currentThread();
lock.lock();
try {
System.out.println(thread.getName() + "获得了锁");
for (int i = 0; i < 5; i++) {
arrayList.add(i);
}
} catch (Exception e) {
} finally {
System.out.println(thread.getName() + "释放了锁");
lock.unlock();
}
};
}.start();
}
}
复制代码
//观察现象:一个线程得到锁后,另外一个线程取不到锁,不会一直等待
public class MyTryLock {
private static List<Integer> arrayList = new ArrayList<Integer>();
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread("线程1") {
@Override
public void run() {
Thread thread = Thread.currentThread();
boolean tryLock = lock.tryLock();
System.out.println(thread.getName()+"======="+tryLock);
if(tryLock){
try {
System.out.println(thread.getName() + "获得了锁");
for(int i = 0;i < 20;i++){
arrayList.add(i);
}
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}
}
}.start();
new Thread("线程2") {
@Override
public void run() {
Thread thread = Thread.currentThread();
boolean tryLock = lock.tryLock();
System.out.println(thread.getName()+"======="+tryLock);
if(tryLock){
try {
System.out.println(thread.getName() + "获得了锁");
for(int i = 0;i < 20;i++){
arrayList.add(i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}
}
}.start();
}
}
复制代码
线程1和线程2共享成员变量arrayList,当线程1获取锁的时候,线程2就获取不到锁,没办法执行它的业务逻辑,只有等线程1执行完毕,释放了锁,线程2才能获取锁,执行它的代码,进而保证了线程安全。安全
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
复制代码
一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操做分开,分红2个锁来分配给线程,从而使得多个线程能够同时进行读操做。bash
/**
* @author 刘俊重
* 若是有一个线程已经占用了读锁,则此时其余线程若是要申请写锁,则申请写锁的线程会一直等待释放读锁。
* 若是有一个线程已经占用了写锁,则此时其余线程若是申请写锁或者读锁,则申请的线程会一直等待释放写锁。
*/
public class MyReentrantReadWriteLock {
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
final MyReentrantReadWriteLock myTest = new MyReentrantReadWriteLock();
new Thread("线程1"){
@Override
public void run(){
myTest.read(Thread.currentThread());
myTest.writer(Thread.currentThread());
}
}.start();
new Thread("线程2"){
@Override
public void run(){
myTest.read(Thread.currentThread());
myTest.writer(Thread.currentThread());
}
}.start();
}
/**
* @Description 读方法
* @Author 刘俊重
* @Date 2017/12/18
*/
private void read(Thread thread){
readWriteLock.readLock().lock();
try {
long start = System.currentTimeMillis();
while (System.currentTimeMillis()-start<=1){
System.out.println(thread.getName()+"===正在执行读操做");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
System.out.println(thread.getName()+"==释放读锁");
}
}
/**
* @Description 写方法
* @Author 刘俊重
* @Date 2017/12/18
*/
private void writer(Thread thread){
readWriteLock.writeLock().lock();
try {
long start = System.currentTimeMillis();
while (System.currentTimeMillis()-start<=1){
System.out.println(thread.getName()+"===正在执行写操做");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
System.out.println(thread.getName()+"==释放写锁");
}
}
}
复制代码
Lock和Synchronized的选择:多线程
程序执行时有主内存,每一个线程工做时也有本身的工做内存。当一个线程开始工做时会从主内存中拷贝一个变量的副本到工做内存中,在工做内存中操做完副本时再更新回主内存。当存在多线程时,若是工做内存A处理完还没来得及更新回主内存以前,工做内存B就从主内存中拉取了这个变量,那么很明显这个变量并非最新的数据,会出现问题。怎么解决呢?可使用volatile,volatile有个最显著的特性就是对它所修饰变量具备可见性,什么意思呢,就是当一个线程修改了变量的值,新的值会马上(立刻)同步到主内存中,其它线程使用时拉取到的就是最新的变量值。尽管volatile能保证变量的可见性,但并不能保证线程安全,由于它不能保证原子性。要想线程安全仍是要用同步或者锁。 有一篇文档写volatile写的很好,贴一下:http://dwz.cn/76TMGW并发
JDK1.5以后引入了高级并发特性,在java.util.concurrent包中,是专门用于多线程并发编程的,充分利用了现代计算机多处理器和多核心的功能以编写大规模并发应用程序。主要包含原子量、并发集合、同步器、可重入锁,并对线程池的建立提供了强力的支持。
public static void main(String[] args) {
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//获取cpu核心数
int num = Runtime.getRuntime().availableProcessors();
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(num);
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(8);
ScheduledExecutorService newSingleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
}
复制代码
说到线程池使用以前再强调一下Runnable的孪生兄弟——Callable,他们两个很像,只是Runnable的run方法不会有任何返回结果,主线程没法得到任务线程的返回值;可是Callable的call方法能够返回结果,可是主线程在获取时是被阻塞,须要等待任务线程返回才能拿到结果,因此Callable比Runnable更强大,那么怎么获取到这个执行结果呢?答案是Future,使用Future能够获取到Callable执行的结果。 如今开始说线程池怎么使用,也有两种方式,一种Runnable的,一种Callable的:
public class TestPoolWithRunnable {
public static void main(String[] args) throws Exception{
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i=0;i<10;i++){
Future<?> submit = pool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行");
}
});
System.out.println("执行结果:"+submit.get()); //全部的执行结果全是null
}
pool.shutdown(); //关闭线程池
}
}
复制代码
/**
* @author 刘俊重
* Callable 跟Runnable的区别:
* Runnable的run方法不会有任何返回结果,因此主线程没法得到任务线程的返回值
* Callable的call方法能够返回结果,可是主线程在获取时是被阻塞,须要等待任务线程返回才能拿到结果
*/
public class TestPoolWithCallable {
public static void main(String[] args) throws Exception{
ExecutorService pool = Executors.newFixedThreadPool(4);
for(int i=0;i<10;i++){
Future<String> future = pool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(500);
return "===="+Thread.currentThread().getName();
}
});
//从Future中get结果,这个方法是会被阻塞的,一直要等到线程任务返回结果
System.out.println("执行结果:"+future.get());
}
pool.shutdown();
}
}
复制代码
如何解决获取执行结果阻塞的问题? 在使用future.get()方法获取结果时,这个方法是阻塞的,怎么提升效率呢?若是在不要求立马拿到执行结果的状况下,能够先将执行结果放在队列里面,待程序执行完毕以后在获取每一个线程的执行结果,示例代码以下:
public class TestThreadPool {
public static void main(String[] args) throws Exception{
Future<?> submit = null;
//建立缓存线程池
ExecutorService cachePool = Executors.newCachedThreadPool();
//用来存在Callable执行结果
List<Future<?>> futureList = new ArrayList<Future<?>>();
for(int i = 0;i<10;i++){
//cachePool提交线程,Callable,Runnable无返回值
//submit = cachePool.submit(new TaskCallable(i));
submit = cachePool.submit(new TaskRunnable(i));
//把这些执行结果放到list中,后面再取能够避免阻塞
futureList.add(submit);
}
cachePool.shutdown();
//打印执行结果
for(Future f : futureList){
boolean done = f.isDone();
System.out.println(done?"已完成":"未完成");
System.out.println("线程返回结果:"+f.get());
}
}
}
复制代码
把submit放在list集合中,线程直线完毕以后再取。
直接使用new Thread().start()的方式,对于通常场景是没问题的,但若是是在并发请求很高的状况下,就会有隐患:
不论是经过Executors建立线程池,仍是经过Spring来管理,都得知道有哪几种线程池:
由以上线程池类型可知,除了CachedThreadPool其余线程池都有饱和的可能,当饱和之后就须要相应的策略处理请求线程的任务,好比,达到上限时经过ThreadPoolExecutor.setRejectedExecutionHandler方法设置一个拒绝任务的策略,JDK提供了AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy几种策略。