java线程

线程.

状态

image-20200309101633824

新建状态(New):

当用 new 操作符创建一个线程时, 例如 new Thread(r),线程还没有开始运行,此时
线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

就绪状态(Runnable)

一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的 start()方法。当线
程对象调用 start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行
run()方法。当 start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行 run()方法,线程还必须同其他线程竞争 CPU 时
间,只有获得 CPU 时间才可以运行线程。因为在单 CPU 的计算机系统中,不可能同时运行
多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。
对多个处于就绪状态的线程是由 Java 运行时系统的线程调度程序(thread scheduler)来调度
的。

运行状态(Running)

当线程获得 CPU 时间后,它才进入运行状态,真正开始执行 run()方法.

阻塞状态(Blocked)

线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用 sleep 方法进入睡眠状态;
2>线程调用一个在 I/O 上被阻塞的操作,即该操作在输入输出操作完成之前不会返回
到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;
......
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出 CPU,这时其他处于就绪状
态的线程就可以获得 CPU 时间,进入运行状态。

死亡状态(Dead)

有两个原因会导致线程死亡:

  1. run 方法正常退出而自然死亡,
  2. 一个未捕获的异常终止了 run 方法而使线程猝死。
    为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使
    用 isAlive 方法。如果是可运行或被阻塞,这个方法返回 true; 如果线程仍旧是 new 状态
    且不是可运行的, 或者线程死亡了,则返回 false

新建线程的方法:

1.继承Thread类实现多线程

run()为线程类的核心方法,相当于主线程的main方法,是每个线程的入口
一个线程调用 两次start()方法将会抛出线程状态异常,也就是的start()只可以被调用一次

class Thread1 extends Thread{

	@Override
	public void run() {
		//迭代 循环
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+"__"+i);	
		}
	}
	
}


public class ThreadTest {
	
	public static void main(String[] args) {
		System.out.println("main start:  "+Thread.currentThread().getName());
		for (int i = 0; i < 10; i++) {
			Thread1 thread1 =new Thread1();
			thread1.start();//多个跑道 并行
		}
		//main 不等了
		System.out.println(" main end ");
	}

}
2通过Runnable接口创建线程类

定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

调用线程对象的start()方法来启动该线程。

public class RunnableThreadTest implements Runnable {

	@Override
	public void run() {

		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "__" + i);
		}

	}
	public static void main(String[] args) {
		System.out.println("main start:  "+Thread.currentThread().getName());
		for (int i = 0; i < 5; i++) {
			RunnableThreadTest runnableThreadTest = new RunnableThreadTest();
			new Thread(runnableThreadTest,"线程"+i).start();
		}
	}

}

通过Callable和Future创建线程

创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值

public interface Callable
{
  V call() throws Exception;
}

创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。)

使用FutureTask对象作为Thread对象的target创建并启动新线程。

调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

public class CallableThreadTest implements Callable<Integer>  
{  
  
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            log.info(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的线程").start();  
            }  
        }  
        try  
        {  
            log.info("子线程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }  
  
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            log.info(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
  
}  

并行参数问题的解决

ThreadLocal (线程本地变量) map

新建ThreadLocal并重写initialValue() 重写initialValue 方法 否者对象为空

调用方法(ThreadLocal定义的变量名)threadLocal.get();

package com.testfan.thread;

import java.util.HashMap;
import java.util.Map;

public class ThreadLocalMapTest {
	
	static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>(){

		@Override
		protected Map<String, Object> initialValue() {
			return new HashMap<String, Object>();
		}
	};
	public static void main(String[] args) {
		
		threadLocal.get().put("1","11");
		dosomethings();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				threadLocal.get().put("1","22");
				dosomethings();
			}
		}).start();
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				threadLocal.get().put("1","33");
				dosomethings();
			}
		}).start();
	}
	
	private static void dosomethings() {
		System.out.println(Thread.currentThread().getName() + "  " +threadLocal.get());
	}

}

线程等待问题

线程等待 join
for (int i = 0; i < 10; i++) {
			Thread1 thread1 =new Thread1();
			thread1.start();//多个跑道 并行
			try {
				thread1.join();//线程等待
			} catch (InterruptedException e) {
				  
				// TODO Auto-generated catch block  
				e.printStackTrace();  
				
			} 
sleep
public static void main(String[] args) {
		System.out.println("main start:  "+Thread.currentThread().getName());
		for (int i = 0; i < 10; i++) {
			Thread1 thread1 =new Thread1();
			thread1.start();//多个跑道 并行
			try {
				thread1.sleep(1);;//线程等待
			} catch (InterruptedException e) {
				  
				// TODO Auto-generated catch block  
				e.printStackTrace();  
				
			} 
线程计数器
public class ThreadDemo2 {
 
    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch latch= new CountDownLatch(5);//使用java并发库concurrent
        //启用5个线程
        for(int i=1;i<=5;i++){
            new Thread(new Runnable(){
                 public void run(){
                     try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                     System.out.println("子线程执行!");
                     latch.countDown();//让latch中的数值减一
                     
                 }
            }).start();
            
        }
        //主线程
        latch.await();//阻塞当前线程直到latch中数值为零才执行
        System.out.println("主线程执行!");
    }
}

countDownLatch不可能重新初始化或者修改CountDownLatch对象内部计数器的值,一个线程调用countdown方法happen-before另外一个线程调用await方法

线程池优化

为什么用线程池:

服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。

线程池为线程生命周期开销问题和资源不足问题提供了解决方案

好处:

在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

线程池的几种方法:

https://www.cnblogs.com/aaron911/p/6213808.html

第一种:定时任务(jenkines的定时任务)

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

实例:

 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
		 scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				System.out.println("111111");
			}
		},0,1,TimeUnit.SECONDS);//0代表延时多久 1代表多久执行一次,TimeUnit.SECONDS代表时间单位

第二种:

	//可以最大开65536, 短任务
	ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

实例:

		for (int i = 0; i < 100000; i++) {
			final int index = i;
			//放了十个线程
			cachedThreadPool.execute(new Runnable() {
				public void run() {
					System.out.println(index+Thread.currentThread().getName());
				}
			});
		}
		fixedThreadPool.shutdown();

第三种:

//按照固定的数目
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);//一次运行3个

实例:

	for (int i = 0; i < 100; i++) {
			final int index = i;
			//放了十个线程
			fixedThreadPool.execute(new Runnable() {
				public void run() {
					System.out.println(index+Thread.currentThread().getName());
				}
			});
		}
		fixedThreadPool.shutdown();

第四种:

//默认一个
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

实例:

		for (int i = 0; i < 100; i++) {
			final int index = i;
			//放了十个线程
			singleThreadExecutor.execute(new Runnable() {
				public void run() {
					System.out.println(index+Thread.currentThread().getName());
				}
			});
		}
		fixedThreadPool.shutdown();
posted @ 2020-05-11 13:46  测开工程师成长之路  阅读(180)  评论(0编辑  收藏  举报