java基础---线程
Posted 周无极
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础---线程相关的知识,希望对你有一定的参考价值。
(一)
一、程序 进程 线程
1、程序:指令集 静态概念
2、进程:操作系统 调度程序 动态概念
3、线程:在进程内多条执行路径
(二)
一、继承Thread + run()
启动: 创建子类对象 +对象.start()
package com.zwj.thread; /** * 模拟龟兔赛跑 1、创建多线程 继承 Thread +重写run(线程体) 2、使用线程: 创建子类对象 + 对象.start() 线程启动 * * @author Administrator * */ public class Rabbit extends Thread { @Override public void run() { //线程体 for(int i=0;i<10;i++){ System.out.println("兔子跑了"+i+"步"); } } } class Tortoise extends Thread { @Override public void run() { //线程体 for(int i=0;i<10;i++){ System.out.println("乌龟跑了"+i+"步"); } } }
package com.zwj.thread; public class RabbitApp { /** * @param args */ public static void main(String[] args) { //创建子类对象 Rabbit rab = new Rabbit(); Tortoise tor =new Tortoise(); //调用start 方法 rab.start(); //不要调用run方法 //rab.run(); tor.start(); //tor.run(); for(int i=0;i<10;i++){ System.out.println("main==>"+i); } } } /* 乌龟跑了0步 乌龟跑了1步 兔子跑了0步 乌龟跑了2步 main==>1 乌龟跑了3步 兔子跑了1步 乌龟跑了4步 main==>2 乌龟跑了5步 兔子跑了2步 乌龟跑了6步 main==>3 乌龟跑了7步 兔子跑了3步 乌龟跑了8步 main==>4 乌龟跑了9步 兔子跑了4步 兔子跑了5步 兔子跑了6步 main==>5 兔子跑了7步 main==>6 兔子跑了8步 main==>7 兔子跑了9步 main==>8 main==>9 */
二、实现Runnable +run()
启动:使用静态代理
1、创建真实角色
2、创建代理角色 Thread+引用
3、代理角色.start()
推荐使用接口:
1、避免单继承局限性
2、便于共享资源
package com.zwj.thread; /** 推荐 Runnable 创建线程 1)、避免单继承的局限性 2)、便于共享资源 使用 Runnable 创建线程 1、类 实现 Runnable接口 +重写 run() -->真实角色类 2、启动多线程 使用静态代理 1)、创建真实角色 2)、创建代理角色 +真实角色引用 3)、调用 .start() 启动线程 * @author Administrator * */ public class Programmer implements Runnable { @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("一边敲helloworld...."); } } }
package com.zwj.thread; public class ProgrammerApp { /** * @param args */ public static void main(String[] args) { //1)、创建真实角色 Programmer pro =new Programmer(); //2)、创建代理角色 +真实角色引用 Thread proxy =new Thread(pro); //3)、调用 .start() 启动线程 proxy.start(); for(int i=0;i<10;i++){ System.out.println("一边聊qq...."+i); } } } /* 一边聊qq....0 一边聊qq....1 一边聊qq....2 一边聊qq....3 一边聊qq....4 一边聊qq....5 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边敲helloworld.... 一边聊qq....6 一边聊qq....7 一边聊qq....8 一边聊qq....9*/
package com.zwj.thread; /** * 方便共享资源 * @author Administrator * */ public class Web12306 implements Runnable { private int num =10; @Override public void run() { while(true){ if(num<=0){ break; //跳出循环 } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } public static void main(String[] args) { //真实角色 Web12306 web = new Web12306(); //代理 Thread t1 =new Thread(web,"路人甲"); Thread t2 =new Thread(web,"黄牛已"); Thread t3 =new Thread(web,"攻城师"); //启动线程 t1.start(); t2.start(); t3.start(); } }/*路人甲抢到了10 路人甲抢到了8 路人甲抢到了7 路人甲抢到了6 路人甲抢到了5 路人甲抢到了4 路人甲抢到了3 路人甲抢到了2 路人甲抢到了1 黄牛已抢到了9 */
三、了解
通过Callable接口实现多线程
优点:可以获取返回值
Callable 和 Future接口
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
Callable和Runnable有几点不同:
(1)Callable规定的方法是call(),而Runnable规定的方法是run().
(2)call()方法可抛出异常,而run()方法是不能抛出异常的。
(3) Callable的任务执行后可返回值,运行Callable任务可拿到一个Future对象,而Runnable的任务是不能返回值的。
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
缺点 :繁琐
思路:
1)、创建 Callable实现类+重写call
2)、借助 执行调度服务 ExecutorService,获取Future对象
ExecutorService ser=Executors.newFixedThreadPool(2);
Future result =ser.submit(实现类对象)
3)、获取值 result.get()
4 )、 停止服务 ser.shutdownNow();
package com.zwj.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 使用Callable创建线程 * @author Administrator * */ public class Call { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建线程 ExecutorService ser=Executors.newFixedThreadPool(2); Race tortoise = new Race("老不死",1000); Race rabbit = new Race("小兔子",500); //获取值 Future<Integer> result1 =ser.submit(tortoise) ; Future<Integer> result2 =ser.submit(rabbit) ; Thread.sleep(2000); //2秒 tortoise.setFlag(false); //停止线程体循环 rabbit.setFlag(false); int num1 =result1.get(); int num2 =result2.get(); System.out.println("乌龟跑了-->"+num1+"步"); System.out.println("小兔子跑了-->"+num2+"步"); //停止服务 ser.shutdownNow(); } } class Race implements Callable<Integer>{ private String name ; //名称 private long time; //延时时间 private boolean flag =true; private int step =0; //步 public Race() { } public Race(String name) { super(); this.name = name; } public Race(String name,long time) { super(); this.name = name; this.time =time; } @Override public Integer call() throws Exception { while(flag){ Thread.sleep(time); //延时 step++; } return step; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getStep() { return step; } public void setStep(int step) { this.step = step; } } /*乌龟跑了-->3步 小兔子跑了-->5步 */
(三)
1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4. 阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;
......
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5. 死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是 可运行的, 或者线程死亡了,则返回false.
二、停止线程
1、自然终止:线程体正常执行完毕
2、外部干涉:
1)、线程类中 定义 线程体使用的标识
2)、线程体使用该标识
3)、提供对外的方法改变该标识
4)、外部根据条件调用该方法即可
package com.zwj.status; public class StopDemo01 { /** * @param args */ public static void main(String[] args) { Study s =new Study(); new Thread(s).start(); //外部干涉 当main线程执行50次时,study线程停止运行 for(int i=0;i<100;i++){ if(50==i){ //外部干涉 s.stop(); } System.out.println("main.....-->"+i); } } } class Study implements Runnable{ //1)、线程类中 定义 线程体使用的标识 private boolean flag =true; @Override public void run() { //2)、线程体使用该标识 while(flag){ System.out.println("study thread...."); } } //3)、对外提供方法改变标识 public void stop(){ this.flag =false; } }
三、阻塞
1、join :合并线程, join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
package com.zwj.status; /** * join:合并线程 * @author Administrator * */ public class JoinDemo01 extends Thread { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { JoinDemo01 demo = new JoinDemo01(); Thread t = new Thread(demo); //新生 t.start();//就绪 //cpu调度 运行 for(int i=0;i<10;i++){ if(5==i){ t.join(); //执行JoinDemo01线程 main线程等待上一个线程执行完后在执行... } System.out.println("main...."+i); } } @Override public void run() { for(int i=0;i<10;i++){ System.out.println("join...."+i); } } } /*main....0 join....0 main....1 join....1 main....2 join....2 main....3 main....4 join....3 join....4 join....5 join....6 join....7 join....8 join....9 main....5 main....6 main....7 main....8 main....9 */
2、yield:暂停自己的线程 static, 该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
package com.bjsxt.thread.status; public class YieldDemo01 extends Thread { /** * @param args */ public static void main(String[] args) { YieldDemo01 demo = new YieldDemo01(); Thread t = new Thread(demo); //新生 t.start();//就绪 //cpu调度 运行 for(int i=0;i<1000;i++){ if(i%20==0){ //暂停本线程 main Thread.yield(); } System.out.println("main...."+i); } } @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("yield...."+i); } } }
3、sleep:休眠,不释放锁
1)、与时间相关:倒计时
2)、模拟网络延时
3)、使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
package com.zwj.status; import java.text.SimpleDateFormat; import java.util.Date; /** * 倒计时 * 1、倒数10个数,一秒内打印一个 * 2、倒计时 * @author Administrator * */ public class SleepDemo01 { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { test1(); Date endTime =new Date(System.currentTimeMillis()+10*1000); long end =endTime.getTime(); while(true){ //输出 System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(endTime)); //等待一秒 Thread.sleep(1000); //构建下一秒时间 endTime =new Date(endTime.getTime()-1000); //10秒以内 继续 否则 退出 if(end-10000>endTime.getTime()){ break; } } } /*倒计时 10 */ public static void test1() throws InterruptedException{ int num =10; while(true){ System.out.println(num--); Thread.sleep(1000); //暂停 1000毫秒等于1秒 if(num<=0){ break; } } } /*10 9 8 7 6 5 4 3 2 1 2017-11-12 12:46:38 2017-11-12 12:46:37 2017-11-12 12:46:36 2017-11-12 12:46:35 2017-11-12 12:46:34 2017-11-12 12:46:33 2017-11-12 12:46:32 2017-11-12 12:46:31 2017-11-12 12:46:30 2017-11-12 12:46:29 2017-11-12 12:46:28*/ }
package com.zwj.status; /** * Sleep模拟 网络延时 线程不安全的类 * @author Administrator * */ public class SleepDemo02 { /** * @param args */ public static void main(String[] args) { //真实角色 Web12306 web= new Web12306(); Web12306 web2 = new Web12306(); //代理 Thread t1 =new Thread(web,"路人甲"); Thread t2 =new Thread(web,"黄牛已"); Thread t3 =new Thread(web,"攻城师"); //启动线程 t1.start(); t2.start(); t3.start(); } } class Web12306 implements Runnable { private int num =10; @Override public void run() { while(true){ if(num<=0){ break; //跳出循环 } try { //500毫秒 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } } /*黄牛已抢到了10 路人甲抢到了9 攻城师抢到了8 路人甲抢到了7 黄牛已抢到了6 攻城师抢到了5 黄牛已抢到了4 路人甲抢到了3 攻城师抢到了2 黄牛已抢到了0 路人甲抢到了1 攻城师抢到了-1*/
(四)
同步:并发 多个线程访问同一份资源 确保资源安全 -->线程安全
synchronized -->同步
一、同步块
synchronized(引用类型|this|类.class){
}
二、同步方法
synchronized
三、死锁: 过多的同步容易造成死锁
package com.zwj.synchronizeds; public class SynDemo01 { /** * @param args */ public static void main(String[] args) { //真实角色 Web12306 web= new Web12306(); //代理 Thread t1 =new Thread(web,"路人甲"); Thread t2 =new Thread(web,"黄牛已"); Thread t3 =new Thread(web,"攻城师"); //启动线程 t1.start(); t2.start(); t3.start(); } } /** * 线程安全的类 * @author Administrator * */ class Web12306 implements Runnable { private int num =10; private boolean flag =true; @Override public void run() { while(flag){ //test5(); test4(); //test3(); 线程安全 锁定正确 同步代码块 //test2(); 线程安全 锁定正确 同步方法 static synchronized //test1(); 线程不安全 } } public void test6(){ if(num<=0){ flag=false; //跳出循环 return ; } //a b c synchronized(this){ try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程不安全 锁定资源不正确 public void test5(){ //a b c synchronized((Integer)num){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //锁定范围不正确 线程不安全 public void test4(){ // c 1 synchronized(this){ //b if(num<=0){ flag=false; //跳出循环 return ; } } // b try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }//a -->1 //线程安全 锁定正确 同步代码块 public void test3(){ //a b c synchronized(this){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程安全 锁定正确 同步方法 static synchronized public synchronized void test2(){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } //线程不安全 public void test1(){ if(num<=0){ flag=false; //Java线程池详解