线程共包括以下5种状态。
1. 新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked) : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead) : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
中断:
Thread类中的方法
public void interrupt() 中断线程
public boolean isInterrupt() 判断线程是否中断
public static boolean interrupted 判断线程是否中断,并且清除当前中断状态
当中断标志被改变为true时,且线程处于 join() wait() sleep() 状态是会触发InterrupedException异常。抛出异常会复原中断标志。
和线程相关的类:
Executors
四种创建线程的构造方法
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
注意:线程池只是为了控制应用中处理某项业务中防止高并发问题带来的线程不安全的发生的概率。在我目前的测试用,还没有发现线程可以重用这个概念,因为线程开启后,用完就关闭了,不可以再次开启的,查看源码发现会每次新创建一个线程用来处理业务。我们可以通过线程池指定处理这项业务最大的同步线程数,比如:Executors.newFixedThreadPool(3);在线程池中保持三个线程可以同时执行,但是注意,并不是说线程池中永远都是这三个线程,只是说可以同时存在的线程数,当某个线程执行结束后,会有新的线程进来。newFixedThreadPool.execute(new ThreadForpools());这句话的含义并不是添加新的线程,而是添加新的处理业务请求进来。至少我当前是这么理解的,没有发现线程可以重复使用。
和返回值相关的类:
Callable接口
Runnable和Callable的区别
Runnable执行方法是run(),Callable是call()
实现Runnable接口的任务线程无返回值;实现Callable接口的任务线程能返回执行结果
call方法可以抛出异常,run方法若有异常只能在内部消化
class TheardRunnable implements Runnable{
@override
public void run(){
System.out.println("aaaaaa");
}
}
class TheardCallable implements Callable{
@override
public String call(){
System.out.println("aaaaaa");
return "aaaaaa";
}
}
class Main{
public static void main(String[] args){
// Executors 无返回参数的使用方式
ExecutorService es = Executors.newCacheTheardPool();
for (int i = 0; i < 5; i ++)
es.executor(new TheardRunnable());
es.shutdown();
// Executors 有返回参数的使用方式
ExecutorService es = Executors.newCacheTheardPool();
ArrayList<Future<String>> results = new ArrayList<Future<String>>();
for ( int i = 0; i <= 5; i ++){
results.add(es.submit( new TheardCallable() ));
}
for ( Future<String> fs : results ){
try{
System.out.println(fs.get());
}catch(){
}finally{
es.shutdown();
}
}
}
}
注意
Callable接口支持返回执行结果,需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取结果;当不调用此方法时,主线程不会阻塞!
如果线程出现异常,Future.get()会抛出throws InterruptedException或者ExecutionException;如果线程已经取消,会抛出CancellationException
wait、notify、notifyAll、sleep、yield、join的区别:
wait 会释放锁。
线程中共享变量的方法
1.使用单个继承Runnable接口的类
在该类中创建 private static int count 变量,在run方法中操作定义的static变量,该变量为同一Runnable接口线程的共享变量
public class Thread5 implements Runnable {
// 此变量为共享变量
private static int count = 20;
@Override
public void run() {
while(count>0){
System.out.println(Thread.currentThread().getName() + "::count :" + count);
count --;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.使用多个继承Runnable接口的类
此时需要添加第三方数据类(不能是基本数据类型,因为其传值为值传递,非引用传递),保存需要共享的数据信息,不同的线程类中 需添加带参数的构造方法,将共享的数据信息 传递到线程类中,并进行相应的操作,此时此 第三方数据类为共享变量
public class MyData {
private String name;
private int age;
public MyData(String name , int age){
this.name = name ;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Thread1 implements Runnable{
// 此变量为共享变量,不能是基本类型,因为基本类型传递为 值传递
private MyData myData;
public Thread1(MyData myData){
this.myData = myData ;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName()+ "::" +myData.getName() + ":" + myData.getAge());
myData.setAge(i);
}
}
}
public class Thread2 implements Runnable{
private MyData myData;
public Thread2(MyData myData){
this.myData = myData ;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName()+ "::" +myData.getName() + ":" + myData.getAge());
}
}
}
public class Main {
public static void main(String[] args) throws Exception{
MyData mydata = new MyData("zhangsan", 24);
Thread thread1 = new Thread(new Thread1(mydata));
Thread thread2 = new Thread(new Thread2(mydata));
thread1.start();
thread2.start();
}
}
2.解决线程中访问共享变量的问题
单个继承Runnable的类
添加额外的static变量,并在while()循环的内部同步该static变量,可保证正确访问共享变量
public class Thread5 implements Runnable {
// 此变量为共享变量
private static int count = 20;
// 此为同步变量
private static String sync = "aa";
@Override
public void run() {
while(count>0){
// 此处进行同步
synchronized(sync){
System.out.println(Thread.currentThread().getName() + "::count :" + count);
count --;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
多个继承Runnable接口的类
3.其他实现安全访问共享变量的方法