线程
来源:互联网 发布:牛股选股器源码 编辑:程序博客网 时间:2024/06/06 15:39
1、概述
(看最后一个图)
进程包含线程;线程是进程的一部分,是轻量级的进程,是不同的执行路径。
多线程:一条路径不能满足要求,另开辟一条路径。
真正的多线程是有多个cpu,有多核。cpu调度哪个程序哪段代码有个调度,调度过程中有个时间片(纳秒级别)。把线程a挂起,再执行线程b,时间片非常的短,看起来像是a和b同时在执行。
什么时候需要执行多线程?
main方法(主线程)执行的时候,还有后台默默执行的gc,这个就是多线程;
异常机制;
2、java实现多线程
就是一些类和接口
模拟多线程:
/** * 模拟龟兔赛跑 1、创建多线程 继承 Thread +重写run(线程体) 2、使用线程: 创建子类对象 + 对象.start() ,这表示线程启动,线程启动不表示线程运行 (调用start()方法,加到线程组里面去了。 它内部由cpu去管控它,cpu去调用它。 而且不要去调用线程类的run方法,那不叫启动线程,而是方法调用。) 总之:想开辟一条新路径,必须调用start()方法,加到线程组里面,由cpu自己管控。 * * @author Administrator * */public class Rabbit extends Thread { @Override public void run() { //线程体 for(int i=0;i<100;i++){ System.out.println("兔子跑了"+i+"步"); } }}class Tortoise extends Thread { @Override public void run() { //线程体 for(int i=0;i<100;i++){ System.out.println("乌龟跑了"+i+"步"); } }}public class RabbitApp { /** * @param args */ public static void main(String[] args) { //创建子类对象 Rabbit rab = new Rabbit(); Tortoise tor =new Tortoise(); //调用start 方法 rab.start(); //不要调用run方法,这个run方法是内部自己调用 //rab.run(); tor.start(); //tor.run(); //这里一共是5条路,加上main,后台的gc和异常。 for(int i=0;i<1000;i++){ System.out.println("main==>"+i); } }}
这种方式实现了静态代理模式。run方法不能对外声明异常
package com.bjsxt.thread.create;/** * 静态代理 设计模式 * 1、真实角色 * 2、代理角色: 持有真实角色的引用 (婚介忙前忙后,但结婚的人还是你) * 3、二者 实现相同的接口 (你租房子找中介公司、你结婚找婚介) * * @author Administrator * */public class StaticProxy { /** * @param args */ public static void main(String[] args) { //创建真实角色,这里没有新增方法,就可以用接口 Marry you =new You(); //创建代理角色 + 真实角色的引用(这里可以用set和get方法,也可以用构造器) WeddingCompany company =new WeddingCompany(you); //执行任务 company.marry(); }}//接口interface Marry{ //void marry();可以省略 public abstract void marry();}//真实角色class You implements Marry{ @Override public void marry() { System.out.println("you and 嫦娥结婚了...."); }}//代理角色class WeddingCompany implements Marry{ private Marry you; public WeddingCompany() { } public WeddingCompany(Marry you) { this.you = you; } //注意是私有的 private void before(){ System.out.println("布置猪窝...."); } private void after(){ System.out.println("闹玉兔...."); } @Override public void marry() { before(); you.marry(); after(); }}
Thread类实现了Runnable接口,Thread类就是代理角色,我们创建一个真实角色就可以了。
真实角色实现Runnable接口+Thread(Runnable target)代理持有真实角色的引用 = 符合代理模式
源码中:run()方法,如果target!=null,就执行target的run()方法。
代理可以有多个,表示资源可以共享
package com.bjsxt.thread.create;/** * 方便共享资源 * @author Administrator * */public class Web12306 implements Runnable { private int num =50; @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(); }}
创建多线程总结:
package com.bjsxt.thread.create;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、线程状态
创建状态:new一下即可
就绪状态:调用start就进入就绪了
运行状态:cpu调度了
终止:线程体执(run方法)行完毕。 下面看线程怎么停止
package com.bjsxt.thread.status;public class StopDemo01 { /** * @param args */ public static void main(String[] args) { Study s =new Study(); new Thread(s).start(); //外部干涉 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; }}
阻塞:
join:合并线程. (等合进来的运行完了。)
package com.bjsxt.thread.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<1000;i++){ if(50==i){ t.join(); //main阻塞... 等于50的时候,让demo执行完毕,再执行main线程 } System.out.println("main...."+i); } } @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("join...."+i); } }}
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); } }}
sleep:休眠,(静态方法)不释放锁。
倒计时:
package com.bjsxt.thread.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 { Date endTime =new Date(System.currentTimeMillis()+10*1000); long end =endTime.getTime(); while(true){ //输出 System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); //format格式化为字符串,parse转换为日期 //等待一秒,顺序也可以放在下面 Thread.sleep(1000); //构建下一秒时间 endTime =new Date(endTime.getTime()-1000); //10秒以内 继续 否则 退出 if(end-10000>endTime.getTime()){ break; } } } public static void test1() throws InterruptedException{ int num =10; while(true){ System.out.println(num--); Thread.sleep(1000); //暂停 if(num<=0){ break; } } }}
网络延迟:
package com.bjsxt.thread.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 =50; @Override public void run() { while(true){ if(num<=0){ break; //跳出循环 } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } }}
线程状态:
设置优先级/** * 优先级:概率,不是绝对的先后顺序 MAX_PRIORITY 10 NORM_PRIORITY 5 (默认) MIN_PRIORITY 1 setPriority() getPriority() * @author Administrator * */
4、线程同步:
java.util.Hashtable - 线程安全的,同步的
StringBuffer – 也是线程安全的,使用了synchronized。这个关键字就是锁,任何线程访问这个方法,先获得这个方法的锁,锁住了门一关别人就访问不了
等线程锁释放了,别的锁才能进来。线程体执行完毕,锁自然就释放了。
访问同一个资源才会有并发问题,访问不同的资源没事。
一般地线程不安全的效率比较高。
JDK的RunTime就是单例中饿汉
package com.bjsxt.thread.syn;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(); } } //线程不安全 锁定范围不正确 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 abc进来只能是依次地等待 //锁定this,this指的是对象 synchronized(this){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程安全 锁定正确 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; //跳出循环 return ; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }}
package com.bjsxt.thread.syn;/** * 单例设计模式:确保一个类只有一个对象 这个对象由类内部创建,外部只能使用这一个对象(JVM只有一个吧,其它类都是对它的引用) GC.. * @author Administrator * */public class SynDemo02 { /** * @param args */ public static void main(String[] args) { JvmThread thread1 = new JvmThread(100); JvmThread thread2 = new JvmThread(500); thread1.start(); thread2.start(); }}class JvmThread extends Thread{ private long time; public JvmThread() { } public JvmThread(long time) { this.time =time; } @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->创建:"+Jvm.getInstance(time)); }}/** * 单例设计模式(的套路) * 确保一个类只有一个对象 * 懒汉式 double checking(经典的双重检查) * 1、构造器私有化,避免外部直接创建对象 * 2、声明一个私有的静态变量 * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 */class Jvm { //声明一个私有的静态变量 private static Jvm instance =null; //懒得去创建对象 //构造器私有化,避免外部直接创建对象 private Jvm(){ } //对getInstance3的改进 //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 public static Jvm getInstance(long time){ // c d e -->效率 提供 已经存在对象的访问效率(线程不需要等待) if(null==instance){ // a b 。a创建了,b进来发现有对象了,就不必等其他线程拿到对象之后它才可以拿,b直接拿对象就行 synchronized(Jvm.class){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误。才会加一个time } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } } }//a return instance; } //这里是同步块 public static Jvm getInstance3(long time){ //a b c d e -->效率不高 c 存在对象也需要等待 //任何时候要获取对象,如果对象存在的话,那也要进下面的方法,也要等待。锁定的是对象,线程b必须等线程a拿到对象之后才可以拿对象,有了一个等待的时间 //这里不能用this,因为静态方法里面没有this synchronized(Jvm.class){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } return instance; } } //这里是同步方法 public static synchronized Jvm getInstance2(long time){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } return instance; } //问题:单线程里面new出来的始终是一个对象,多线程的话就要加synchronized public static Jvm getInstance1(long time){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } //两个线程进来,就有可能创建两个对象 instance =new Jvm(); } return instance; }}
package com.bjsxt.thread.syn;/** * 单例创建的方式 * 1、懒汉式 * 1)、构造器私有化 * 2)、声明私有的静态属性 * 3)、对外提供访问属性的静态方法,确保该对象存在 * * @author Administrator * */ //懒汉式public class MyJvm { private static MyJvm instance; private MyJvm(){ } //多线程的难点:又要提高效率,又要注意安全 public static MyJvm getInstance (){ if(null==instance){ //提供效率 synchronized(MyJvm.class){ if(null==instance){ //安全 instance =new MyJvm(); } } } return instance; }}/** * 饿汉式 1)、构造器私有化 * 2)、声明私有的静态属性,同时创建该对象 * 3)、对外提供访问属性的静态方法 * @author Administrator * */class MyJvm2 { //这里是线程安全的, //加载MyJvm2的时候instance就初始化 private static MyJvm2 instance =new MyJvm2(); private MyJvm2(){ } public static MyJvm2 getInstance (){ return instance; }}/** * 类在使用的时候加载 ,延缓加载时间 * @author Administrator * */class MyJvm3 { //对MyJvm2的改进,提升效率的 //内部类 //加载MyJvm3的时候不一定会加载JVMholder,只有用到的时候(这里是调用getInstance时)加载。不调用该方法,JVMholder永远不会被加载 //可以延缓加载时间 private static class JVMholder{ private static MyJvm3 instance =new MyJvm3(); } private MyJvm3(){ } public static MyJvm3 getInstance (){ return JVMholder.instance; }}
死锁:过多的同步造成死锁
即:不给我钱我不给你货,你不给我货我就不给你钱
同一份资源,在线程A中用了,又在线程B中用了,就可能会造成死锁。互相不释放
package com.bjsxt.thread.syn;/** * 过多的同步方法可能造成死锁 * @author Administrator * */public class SynDemo03 { /** * @param args */ public static void main(String[] args) { Object g =new Object(); Object m = new Object(); Test t1 =new Test(g,m); Test2 t2 = new Test2(g,m); Thread proxy = new Thread(t1); //多态不能调用新增方法 Thread proxy2 = new Thread(t2); proxy.start(); proxy2.start(); }}class Test implements Runnable{ Object goods ; Object money ; public Test(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { while(true){ test(); } } public void test(){ synchronized(goods){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(money){ } } System.out.println("一手给钱"); }}class Test2 implements Runnable{ Object goods ; Object money ; public Test2(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { while(true){ test(); } } public void test(){ synchronized(money){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(goods){ } } System.out.println("一手给货"); }}
生产者消费者模式(不是设计模式,是解决多线程的问题)
如何解决死锁,看看生产者消费者模式。
先生产再消费,有东西了再消费,没有东西先生产(用的是同一份资源)
package com.bjsxt.thread.pro;/** 一个场景,共同的资源 生产者消费者模式 信号灯法 wait() :等待,释放锁 。 sleep 不释放锁 notify()/notifyAll():唤醒 他俩必须要与 synchronized一起使用 * @author Administrator * */public class Movie { private String pic ; //信号灯 //flag -->T 生产生产,消费者等待 ,生产完成后通知消费 //flag -->F 消费者消费 生产者等待, 消费完成后通知生产 private boolean flag =true; /** * 播放 * @param pic */ public synchronized void play(String pic){ if(!flag){ //生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //开始生产 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("生产了:"+pic); //生产了左青龙 //生产完毕 this.pic =pic; //通知消费 this.notify(); //生产者停下 this.flag =false; } public synchronized void watch(){ if(flag){ //消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //开始消费 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("消费了"+pic); //消费完毕 //通知生产 this.notifyAll(); //消费停止 this.flag=true; }}
5、任务调度
import java.util.Date;import java.util.Timer;import java.util.TimerTask;/** 了解 Timer() schedule(TimerTask task, Date time) schedule(TimerTask task, Date firstTime, long period) 自学 quartz * @author Administrator * */public class TimeDemo01 { /** * @param args */ public static void main(String[] args) { Timer timer =new Timer(); timer.schedule(new TimerTask(){ @Override public void run() { System.out.println("so easy...."); }}, new Date(System.currentTimeMillis()+1000), 200); }}
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 常用命令:cd/rm & 创建和删除目录
- .Net Framework
- HDU-2955 01背包
- [Python基础]列表、元祖和字典
- 内联函数背景、例子、与普通函数的区别及要注意的地方 ------新标准c++程序设计
- 线程
- 洛谷 P2929 [USACO09HOL]传输延迟Transmission Delay——dp
- Android关于looper的几个方法的个人理解
- PHP初识
- 转场动画
- 动态读取properties文件,并且修改其中的属性
- 123. Best Time to Buy and Sell Stock III
- 萌新的C#学习(1)
- init进程源码分析