Java OOP——第七章 多线程
1、进程:是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间);
Eg:用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。当用户再次点击左面的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间。目前操作系统都支持多进程。
◆注;用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间。
◆进程的特点:进程是系统运行程序的基本单位;
每一个程序都有自己独立的一块内存空间、一组系统资源;
每一个进程的内部数据和状态都是完全独立的;
2、线程:是进程中执行元算的最小单位;可以完成一个独立的顺序控制流程;每个进程中,必须至少建立一个线程(主线程)来作为程序运行的入口点;
◆附加:
线程:是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源, 但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程, 同一进程中的多个线程之间可以并发执行。 |
|
线程: 1、线程是轻量级的进程 2、线程没有独立的地址空间(内存空间) 3、线程是由进程创建的(寄生在进程) 4、一个进程可以拥有多个线程-->这就是我们常说的多线程编程 |
线程的几种状态: a、创建状态(new) b、就绪状态(Runnable) c、运行状态(Running) d、阻塞状态(Blocked) e、死亡状态(Dead) |
3、多线程:如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”;多个线程交替占用CPU资源,而非真正的并行执行
●多线程好处:
a) 充分利用CPU的资源
b) 简化编程模型
c) 带来良好的用户体验
4、JAVA中实现多线程:
●Thread类:Java提供了java.lang.Thread类支持多线程编程
◆ Thread类常用的方法:
构造方法 |
说 明 |
Thread() |
分配新的Thread()对象 |
Thread(Runnable target) |
分配新的Thread()对象。tarage为run()方法被调用的对象; |
Thread(Runnable target,String nasme) |
分配新的Thread()对象。tarage为run()方法被调用的对象;name为新线程的名称 |
void run() |
执行任务操作的方法 |
void start() |
使该线程开始执行,JAVA虚拟机调用线程的run()方法 |
static void sleep(long millis) |
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) |
String getName() |
返回线程的名字 |
Int getPriority() |
返回线程的优先级 |
void setPriority(int newPriority) |
更改线程的优先级 |
static Thread currentThread() |
返回当前正在执行的线程对象的引用 |
void join() |
等待该线程终止 |
static void yield() |
暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() |
中断线程 |
boolean isAlive() |
测试线程是否处于活动状态 |
5、主线程:在JAVA线程启动时,一个线程立即运行该线程称为程序的主线程;每个线程至少有一个主线程;他是程序开始时就执行;
◆ main()方法即为主线程入口
◆产生其他子线程的线程
◆必须最后完成执行,因为它执行各种关闭动作
★主线程可以由一个Thread对象控制,需要调用方法Thread.currentThread()获得他的一个引用,
语法:static Thread currentThread()
Eg:
public static void main(String args[]) {
Thread t= Thread.currentThread(); //获得主线程对象
System.out.println("当前线程是: "+t.getName());
t.setName("MyJavaThread"); //设置线程名
System.out.println("当前线程名是: "+t.getName()); //获取线程名
}
◆在Java中创建线程的两种方式:
★继承java.lang.Thread类:继承Thread类,并重写run函数
★实现java.lang.Runnable接口:实现Runnable接口,并重写run函数
◆使用线程的步骤:
★定义一个线程:同时指明这个线程所要执行的代码;
★创建线程对象;
★启动线程
★终止线程
6、继承Thread类创建线程:
●步骤: ★定义MyThread类继承Thread类 ★重写run()方法,编写线程执行体 ★创建线程对象,调用start()方法启动线程 |
Eg: public class MyThread extends Thread{ //继承Thread类 //重写run()方法 public void run(){ //run()方法中编写线程执行的代码 for(int i=1;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); //启动线程 } |
多个线程交替执行,不是真正的“并行” 线程每次执行时长由分配的CPU时间片长度决定 Eg:MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); t1.start(); t2.start(); |
|
直接调用run()和start()区别: start():启动线程; run():调用实例方法; |
|
7、实现Runnable接口创建线程:
●步骤: ★定义MyRunnable类实现Runnable接口,并实现Runnable接口的run()方法在run()方法中实现输出数据; ★创建MyRunnable类的对象myRunnable; ★创建一个Thread类的对象myThread,将myRunnable对象作为Thread类构造方法的参数传入; ★调用myThread对象的start()方法启动线程; |
|
Eg:
public class MyRunnable implements Runnable{ //实现Runnable接口 public void run(){ for(int i=1;i<100;i++){ // run()方法中编写线程执行的代码 System.out.println(Thread.currentThread().getName()+":"+i); } } } public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); //创建线程对象 Thread myThread = new Thread(myRunnable); thread.start(); //启动线程 } |
|
■用实现Runnable接口的特点: 1、用实现Runnable接口的方法创建对象可以避免java单继承机制带来的局限; 2、用实现Runnable接口的方法,可以实现多个线程共享同一段代码(数据);因此建议大家如果你的程序有同步逻辑需求,则使用Runnable的方法来创建线程。 |
|
比较两种创建线程的方式:推荐使用实现Runnable接口方式创建线程 |
|
★继承Thread类: 编写简单,可直接操作线程 适用于单继承 |
★实现Runnable接口: 避免单继承局限性 便于共享资源 |
附加:从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别, ★从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口,区别如下: 1、尽可能使用实现Runnable接口的方式来创建线程 2、在使用Thread的时候只需要new一个实例出来,调用start()方法即可以启动一个线程, 如: Thread test=new Thread(); test.start(); 3、在使用Runnable的时候需要先new一个实现Runnable的实例,之后用Thread调用,如: Test implements Runnable Test t=new Test(); Thread test=new Thread(t); tset.start(); |
|
注意:不管是通过继承Thread,还是通过实现Runnable接口创建线程,它们的一个对象只能启动(即:start())一次。否则就会有(IllegaiIThreadStateException)异常抛出。 |
8、线程的状态:
1. 创建状态:在程序中用构造方法创建了一个线程对象后,新的线程对象就处于创建状态,此时,它已经获取了相应的资源,但还没有处于可运行状态,这时可以通过Thread类的方法来设置线程对象的属性,
如:设置线程名(setName())、设置线程优先级(setPriority())等。
2. 就绪状态:线程创建之后,就可以通过调用start()方法启动线程,即进入就绪状态。此时,线程将进入线程队列排队,等待CPU资源,这表明它已经具备了运行条件,在未获得CPU资源时,仍不能真正执行。
◆举例来说,去医院看病,某主任的专家号每天只有20个,挂上号的病人还需在分诊处等待叫号。这里每个挂到专家号的病人可以看成一个就绪状态的线程。
3.运行状态:当就绪状态的线程获得CPU资源时,即可转入运行状态,执行的run()方法。对于只有一个CPU的机器而言,任何时刻只能有一个处于运行状态的线程占用CPU,即获得CPU资源。
◆延续上面医院看病的例子,被叫到的病人才能真正就诊,而每个主任专家在一个时刻只能为一个病人看病。
4. 阻塞状态:一个正在运行的线程因某种原因不能继承运行时,进入阻塞状态。阻塞状态是一种“不可运行”的状态,而处于这种状态的线程在得到一个特定的事件之后会转回可运行状态。
◆举例来说,轮到小张看病了,医生为查明原因要求他去做个化验,医生得到化验结果后才能继续诊断,如果把医生给小张看病看作一个线程,该线程此时即处于阻塞状态。
●可能使线程暂停执行的条件:
★线程优先级比较低,因此它不能获得CPU资源。
★使用sleep()方法使线程休眠。
★通过调用wait()方法,使线程等待。
★通过调用yield()方法,线程显式出让CPU控制权。
★线程由于等待一个文件I/O事件被阻塞。
5.死亡状态:一个线程的run()方法运行完毕,线程则进入死亡状态。处于死亡状态的线程不具有继承运行的能力。
9、线程调度:线程调度指按照特定机制为多个线程分配CPU的使用权;
●线程优先级:线程靠抢CPU时间片而执行,谁抢的多谁利用CPU的时间就多也就执行得快。而决定这个争抢能力的就是线程的优先级;
●线程优先级由1~10表示,1最低,10代表优先级最高,默认优先级为5。
这些优先级对应一个Thread类的公用静态常量;优先级高的线程获得CPU资源的概率较大;
线程的优先级可以通过getPriority()方法获取,setPriority(int grade)方法更改,参数表示要设置的优先级,他必须是1~10的整数;
MAX_PRORITY(最大优先级),MIN_PRORITY(最小优先级),NORM_PRORITY(默认优先级);
●线程调度的方法:
方法 |
说 明 |
setPriority(int newPriority) |
更改线程的优先级 |
static void sleep(long millis) |
在指定的毫秒数内让当前正在执行的线程休眠 |
void join() |
等待该线程终止 |
static void yield() |
暂停当前正在执行的线程对象,并行其他线程 |
void interrupt() |
中断线程 |
boolean isAlive() |
测试线程是否处于活动状态 |
●线程休眠:在一个程序中一个线程允许进行暂时休眠,直接调用Thread sleep()方法即可实现线程的休眠;
sleep()方法语法定义:public static void sleep(long millis)
◆让线程暂时睡眠指定时长,线程进入阻塞状态;
◆睡眠时间过后线程会再进入可运行状态;
Eg: public class Wait {
public static void bySec(long s) {
for (int i = 0; i < s; i++) {
System.out.println(i + 1 + "秒");
try {
Thread.sleep(1000); //线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}}}}
★millis为休眠时长,以毫秒为单位
★调用sleep()方法需处理InterruptedException异常
●线程的强制运行:使当前线程暂停执行,等待其他线程结束后再继续执行本线程:
它的三个重载语法语法:
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
◆millis:以毫秒为单位的等待时长
◆nanos:要等待的附加纳秒时长
◆需处理InterruptedException异常
Eg:
public static void main(String[] args) { Thread temp = new Thread(new MyThread()); temp.start(); for(int i=0;i<20;i++){ if(i==5){ try { temp.join(); //阻塞主线程,子线程强制执行 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"运行:"+i); } //省略代码…… } |
结果:
|
●线程的礼让:
◆yield()方法可以暂停当前线程,允许其他具有相同优先级的线程获得运行机会;
◆该线程处于就绪状态,不转为阻塞状态;此时系统选择其他想相同或更高优先级线程允许,若无其他相同或更高优先级线程,则该线程继续运行;
语法:public static void yield()
注:使用yield()的线程礼让只是提供一种可能,但是不能保证一定会实现礼让;
Eg:
|
10、线程的同步:当多个线程共同操纵同一个共享资源的时候,会导致数据不一致的情况;当多个线程共同操纵同一个共享资源的时候,一个线程未完成操作的时候,其他线程修改的数据,会导致数据不安全的情况;
●多线程共享数据引发的问题:
●同步方法:
◆使用synchronized修饰的方法控制对类成员变量的访问 语法:
◆synchronized(同步关键字)就是为当前的线程声明一个锁
|
◆ syncObject为需同步的对象,通常为this,效果与同步方法相同
|
◆多个并发线程访问同一资源的同步代码块时注意: 1、当多个并发线程访问同一个对象object的synchronized(this)同步代码块时,同一时刻只能有一个线程得到执行,其他线程必须等待当前线程执行完毕之后才能执行该代码块; 2、当一个线程访问一个object的synchronized(this)同步代码块时,其他线程对object中所有其他synchronized(this)同步代码块的访问被阻塞,即该线程获得这个object的对象锁,其他线程对该object对象所有同步代码部分的访问被暂时阻塞; 3、当一个线程访问一个object的synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码; 综上所述:synchronized就是为当前线程声明一个锁,获得这个锁的线程可以执行代码块里的指令,其他的线程只能等待解锁,然后才能执行相同的操作; |
◆同步代码块的应用场景:假设某线程是非线程安全的,而且该类是第三方创建的或者是从内置库导入的,所以不能获取他的源代码,这样,无法在相关方法前面加synchronized修饰符; 怎样使该类的一个对象同步化? 解决方案:只需将对调用该方法的代码放入一个synchronized块内就可以了; ◆关键代码: synchronized(同步对象){ //调用第三方非同步方法的代码 } |
11、线程安全的类型:
查看ArrayList类的add()方法定义: public boolean add(E e) { ensureCapacityInternal(size + 1);// 集合扩容,确保能新增数据 elementData[size++] = e; //在新增位置存放数据 return true; } |
||||||||||||||||
ArrayList类的add()方法为非同步方法; 当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题 |
||||||||||||||||
ArrayList为非线程安全的类型 |
||||||||||||||||
|
12、常用类型对比:
◆Hashtable && HashMap
◆Hashtable:继承关系,实现了Map接口,Hashtable继承Dictionary类
特点:线程安全,效率较低
键和值都不允许为null
◆HashMap:继承关系:实现了Map接口,继承AbstractMap类
特点: 非线程安全,效率较高
键和值都允许为null
◆ StringBuffer && StringBuilder:前者线程安全,后者非线程安全
13、附加1:死锁问题:
* 死锁:两个线程都在等待对方先完成,造成程序的停滞; * 产生死锁的条件: * 两个或两个以上的线程在活动; * 某个线程拿到一个锁以后,还想拿第二个锁,造成锁的嵌套? *解决方法: * 当前线程先释放自己的锁? * 尽量减少同步方法或同步代码块的嵌套? |
|
Eg:两个小孩互换玩具; |
|
Eg:此示例不会产生死锁; /** * 模拟死锁 */ public class Test { public static void main(String[] args) { Thread tang=new Thread(new Tang()); Thread dou=new Thread(new Dou()); tang.start(); dou.start(); } } /** * 堂堂 */ class Tang implements Runnable{ Object bobby=new Object();//芭比娃娃 Object duck=new Object();//玩具 @Override public void run() { synchronized (bobby) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (duck) { } System.out.println("堂堂把芭比娃娃给豆豆玩!"); } } } /** * 豆豆 */ class Dou implements Runnable{ Object bobby=new Object();//芭比娃娃 Object duck=new Object();//玩具 @Override public void run() { synchronized (duck) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (bobby) { } System.out.println("豆豆把玩具鸭给堂堂玩"); } } } |
Eg:此示例产生了死锁 /** * 模拟死锁(产生了死锁) */ public class Test1 { public static void main(String[] args) { Object bobby=new Object(); Object duck=new Object(); Thread tang=new Thread(new Tangtang(bobby,duck)); Thread dou=new Thread(new Doudou(bobby,duck)); tang.start(); dou.start(); } } /** * 堂堂 */ class Tangtang implements Runnable{ Object bobby;//芭比娃娃 Object duck;//玩具 //构造函数 public Tangtang(Object bobby,Object duck) { super(); this.bobby=bobby; this.duck=duck; } @Override public void run() { synchronized (bobby) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (duck) { } System.out.println("堂堂把芭比娃娃给豆豆玩!"); } } } /** * 豆豆 */ class Doudou implements Runnable{ Object bobby;//芭比娃娃 Object duck;//玩具 //构造函数 public Doudou(Object bobby,Object duck) { super(); this.bobby=bobby; this.duck=duck; } @Override public void run() { synchronized (duck) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (bobby) { } System.out.println("豆豆把玩具鸭给堂堂玩"); } } } |
14、附加2:生产者和消费者的问题: 生产者不断生产,消费者不断取走生产者的产品;生产者生产出信息之后将其放到一个区域中,之后消费者从此区域取走数据;
Eg:一个轮流录入电影和读取电影信息的程序 变形金刚——一部科幻电影 神偷奶爸——一部3D动画片 分析: 生产者:录入信息; 消费者:读取信息; |
|
/** * 电影类 */ public class Movie { private String name;//电影的名称 private String info;//电影的描述 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } } |
/** * 生产者 */ public class Producer implements Runnable { private Movie movie=null; private boolean flag=false; //构造函数 public Producer(Movie movie) { super(); this.movie=movie; } @Override public void run() { //循环录入50遍电影数据,两部电影交替录入 for (int i = 0; i < 50; i++) { if(flag) { this.movie.setName("变形金刚"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.movie.setInfo("一部科幻电影!"); flag=false; }else { this.movie.setName("神偷奶爸"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.movie.setInfo("一部3D动画电影"); flag=true; } } } } |
/** * 测试类 */ public class Test { public static void main(String[] args) { Movie movie=new Movie(); //生产者线程对象 Thread producer=new Thread(new Producer(movie)); //消费者线程对象 Thread consumer=new Thread(new Consumer(movie)); producer.start(); consumer.start(); } } |
|
/** * 消费者 */ public class Consumer implements Runnable { private Movie movie=null; //构造函数 public Consumer(Movie movie) { super(); this.movie=movie; } @Override public void run() { //循环显示50次电影信息 for (int i = 0; i <50; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.movie.getName()+"— "+this.movie.getInfo()); } } } |
输出结果:(会出现电影名与类型匹配错误的情况(生产与消费不同步)) |
/** * 生产者 */ public class Producer implements Runnable { private Movie movie=null; private boolean flag=false; //构造函数 public Producer(Movie movie) { super(); this.movie=movie; } @Override public void run() { //循环录入50遍电影数据,两部电影交替录入 for (int i = 0; i < 50; i++) { if(flag) { this.movie.set("变形金刚", "一部科幻电影"); flag=false; }else { this.movie.set("神偷奶爸", "一部3D动画电影"); flag=true; } } } } |
/** * 消费者 */ public class Consumer implements Runnable { private Movie movie=null; //构造函数 public Consumer(Movie movie) { super(); this.movie=movie; } @Override public void run() { //循环显示50次电影信息 for (int i = 0; i <50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.movie.get(); } } }
|
package lianxi0Producer; /** * 电影类 */ public class Movie { private String name;//电影的名称 private String info;//电影的描述 public String getName() { return name; } public String getInfo() { return info; } //同步写入 public synchronized void set(String name,String info) { this.name=name; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.info=info; } //同步获取 public synchronized void get() { System.out.println(this.getName()+"--"+this.getInfo()); } } |
/** * 测试类 */ public class Test { public static void main(String[] args) { Movie movie=new Movie(); //生产者线程对象 Thread producer=new Thread(new Producer(movie)); //消费者线程对象 Thread consumer=new Thread(new Consumer(movie)); producer.start(); consumer.start(); } } |
输出结果:(没有实现交替输出(需要调用Object类的两个方法)) |
notify() 唤醒正在等待对象监视器的单个线程。 Wait():导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。 |
|
/** * 生产者 */ public class Producer implements Runnable { private Movie movie=null; private boolean flag=false; //构造函数 public Producer(Movie movie) { super(); this.movie=movie; } @Override public void run() { //循环录入50遍电影数据,两部电影交替录入 for (int i = 0; i < 50; i++) { if(flag) { this.movie.set("变形金刚", "一部科幻电影"); flag=false; }else { this.movie.set("神偷奶爸", "一部3D动画电影"); flag=true; } } } } |
/** * 消费者 */ public class Consumer implements Runnable { private Movie movie=null; //构造函数 public Consumer(Movie movie) { super(); this.movie=movie; } @Override public void run() { //循环显示50次电影信息 for (int i = 0; i <50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.movie.get(); } } }
|
/** * 电影类 */ public class Movie { private String name;//电影的名称 private String info;//电影的描述 private boolean flag=true;//设置标志位置,控制生产者生产,消费者消费 public String getName() { return name; } public String getInfo() { return info; } //同步写入 public synchronized void set(String name,String info) { if(!flag) { try { super.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name=name; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.info=info; flag=false;//重置标志位,让消费者消费 super.notify(); } //同步获取 public synchronized void get() { if(flag) { try { super.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(this.getName()+"— "+this.getInfo()); flag=true; super.notify(); } } |
/** * 测试类 */ public class Test { public static void main(String[] args) { Movie movie=new Movie(); //生产者线程对象 Thread producer=new Thread(new Producer(movie)); //消费者线程对象 Thread consumer=new Thread(new Consumer(movie)); producer.start(); consumer.start(); } } |
输出结果: |
15、附加:线程池:
●使用线程池的理由:
◆线程缺乏统一管理,占用过多的系统资源;
◆缺乏更多的功能,如:定期执行、定时执行等;
●线程池的优点:
◆重用存在的线程、减少对象的创建、消亡的开销;
◆有效控制最大并发数,提高系统资源使用率;
◆定时执行、定期执行;
●线程池所在的包:java.util.concurrent; :顶级接口是Executor,真正的线程池接口是ExecutorService;
Java.util.concurrent.Executors类提供创建线程池的方法;
方法名 |
说明 |
newCachedThreadPool() |
创建一个可缓存的线程池,有任务时才创建新任务; |
newSingleThreadExecutor() |
创建一个单线程池 |
newFixedThreadPool(int nThreads) |
创建一个固定长度的线程池,空闲线程池会一直保留,参数nThreads设定线程池中线程的数目 |
newScheaduledThreadPool(int corePoolSize) |
创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务; |
●使用newCacherThreadPool()方法创建线程池1:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 使用newCacherThreadPool()方法创建线程池 * @author 逆風〠飛翔 * */ public class Test { public static void main(String[] args) { //创建一个线程池 ExecutorService cachedThreadPool= Executors.newCachedThreadPool(); //在线程池中执行10个任务 for (int i = 0; i < 10; i++) { cachedThreadPool.execute(new MyRunnable(i)); try { Thread.sleep(1000); //每执行一次休眠一秒 } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyRunnable implements Runnable{ int num; public MyRunnable(int num) { super(); this.num=num; } @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+num); } } |
输出结果:pool代表线程池 (10个任务被同一个线程执行)
|
●使用newCacherThreadPool()方法创建线程池2:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 使用newCacherThreadPool()方法创建线程池 * @author 逆風〠飛翔 * */ public class Test { public static void main(String[] args) { //创建一个线程池 ExecutorService cachedThreadPool= Executors.newCachedThreadPool(); //在线程池中执行10个任务 for (int i = 0; i < 10; i++) { cachedThreadPool.execute(new MyRunnable(i)); // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } } } } class MyRunnable implements Runnable{ int num; public MyRunnable(int num) { super(); this.num=num; } @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+num); } }
|
结果:(更多的线程参与执行,有些线程执行了两次, eg:线程8执行完7后,又继续执行了9, 省去创建一个新的线程,当有一个线程闲置下来后不需要去销毁,又去执行了一个新的任务) |
●使用newSingleThreadExecutor()方法创建单线程池
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 使用newSingleThreadExecutor()方法创建单线程池 */ public class Test1 { public static void main(String[] args) { //创建一个单线程池 ExecutorService singleThreadPool= Executors.newSingleThreadExecutor(); //在线程池中执行10个任务 for (int i = 0; i < 10; i++) { singleThreadPool.execute(new MyRunnable(i)); // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } } } } class MyRunnable implements Runnable{ int num; public MyRunnable(int num) { super(); this.num=num; } @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+num); } } |
输出结果: |
●使用newFixedThreadExecutor()方法创建指定个数线程池
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 使用newFixedThreadExecutor()方法创建单线程池 */ public class Test2 { public static void main(String[] args) { //创建一个线程池 ExecutorService fixedThreadPool= Executors.newFixedThreadPool(3); //在线程池中执行10个任务 for (int i = 0; i < 10; i++) { fixedThreadPool.execute(new McRunnable(i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class McRunnable implements Runnable{ int num; public McRunnable(int num) { super(); this.num=num; } @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+num); } } |
输出结果:(只有三个线程在执行) |
●
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * 使用newSchediledThreadPool()方法来创建线程池 */ public class Test3 { public static void main(String[] args) { //创建三个线程 ScheduledExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(3); System.out.println("*************开始执行*************"); scheduledThreadPool.scheduleAtFixedRate(new MdRunnable(), 5, 2, TimeUnit.SECONDS); } } class MdRunnable implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"延时5s执行,每2s执行一次"); } } |
●ThreadPoolExecutor类
◆构造器中各个参数的含义:
★corePoolSize:核心池的大小;
★maximumPoolSize:线程池最大线程数;
★keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止;
★unit:参数keepAliveTime的时间单位;
★workQueue:一个阻塞队列,用来存储等待执行的任务;
★threadFactory:线程工厂,主要用来创建线程;
★handler:表示当拒绝处理任务时的策略;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 创建自定义线程池 */ public class Test4 { public static void main(String[] args) { //创建一个自定义线程池(核心线程数,最大线程数,超出的线程消亡的时间,指定毫秒数,阻塞队列) ThreadPoolExecutor executor=new ThreadPoolExecutor(5, 7, 3000, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<Runnable>(4)); //执行12个任务 for (int i = 1; i <=12; i++) { //i为传入当前执行的数量 executor.execute(new MmRunnable(i)); System.out.println("线程池中的线程数:"+executor.getPoolSize()+ ",队列中等待执行的任务:"+executor.getQueue().size()+ ",已执行完成的任务数:"+executor.getCompletedTaskCount()); } executor.shutdown();//关闭线程池 } } class MmRunnable implements Runnable{ int num;//第几个任务 public MmRunnable(int num) { super(); this.num=num; } @Override public void run() { System.out.println("正在执行任务"+num); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("任务"+num+"执行完毕!"); } } |
运行结果: (会创建第12个线程所以会报错) |
16、附加:
●进程和线程的基本概念和原理: 一个进程至少包含一个线程,即主线程。 多个线程的执行时间是由CPU的时间片决定的,时长是随机的。 在单CPU计算机中,一个进程的多个线程是交替执行的,在某一时刻,只能执行一个线程。 |
|
●线程是进程的一个执行路径,而不是一个程序, 线程是进程中执行运算的最小单位, 多线程用于实现并发, 单CPU的计算机中,CPU每个时刻只能执行一条指令,将CPU的执行时间分成多个时间片,分配给不同的线程。 |
|
●线程的创建和启动。当线程被创建后,执行start()方法后,线程处于可运行状态。 当线程执行完一个时间片后继续回到就绪状态,等待CPU再次分配时间片,而不存在一个队列。 线程休眠后,处于阻塞状态。 |
|
●线程的创建和启动,线程类的定义有继承Thead类和实现Runable接口两种方式, 通过实现run()方法编写线程体, 已启动的线程不能重复调用start()方法,否则会报IllegalThreadStateException异常。 |
|
●当线程对象调用start()方法后,进入就绪状态, 当CPU分配时间片后即可执行,可以实现多线程并发, 而直接调用run()方法和调用普通方法一样,只有主线程一条执行路径,不能实现并发执行。 |
|
●eg: 分析: 本题考察线程的状态。创建一个线程对象后,线程对象处于新建状态,执行start()方法后,处于就绪状态。休眠时处于阻塞状态,休眠后处于就绪状态,执行输出语句时处于执行状态。 |
Eg: 分析: 本题主要考查线程的强制执行。当主线程 main方法执行System.out.println(a);这条语句时,线程还没有真正开始运行,或许正在为它分配资源准备运行。因为为线程分配资源需要时间,而main方法执行完t.start()方法后继续往下执行System.out.println(a);,这个时候得到的结果是a还没有被改变的值0 。怎样才能让输出结果为5, join() 方法提供了这种功能。它能够使调用该方法的线程在此之前执行完毕。 |
●eg: 分析:本题考察线程的创建。在自定义线程中如果没有重写run()方法,则默认执行父类Thread中的run()方法,因此不会出现编译错误,也无任务输出结果, |
●Eg: 分析:本题考察如何获取当前线程。线程对象mt直接调用了run()方法,没有使用start()方法启动线程。因此本题目中只启动了主线程和new Thread(mt,"线程"),主线程的线程名为main. |
Eg: 分析:sleep()方法,它不能终止一个线程,它是将线程睡眠一段时间。当时间结束以后,线程就会再次启动。它是不会交出线程锁的 |
Eg: 分析: 因为currentThread()是静态方法,使用类名可直接调用。 |
Eg: 分析: 本题考察线程安全的类型。当多个线程共同操作Counter类的成员变量count时,会引出线程安全问题。解决问题的办法就是对addCount()方法加锁。如果将count变量定义在addCount()方法中,就不会存在多线程共同操作的数据,不会引发并发问题,本题答案为bc |
Eg; D:一个类中可以有多个同步方法或同步代码块; |
●synchronized用来定义同步方法或同步代码块,解决多个线程共享资源时带来的问题,同步后,只允许一个线程进入同步方法或同步代码块。
对于synchronized关键字最精准的描述:保证在某个时刻只有一个线程可以访问方法或对象; |
|
●synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; 4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。 |
Java OOP——第七章 多线程的更多相关文章
- “全栈2019”Java异常第七章:try-catch-finally组合方式
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...
- Java oop 第13章_多线程
第13章_多线程 一. 多线程相关的概念: 程序:由某种编程语言开发可执行某些功能的代码组合,它是静态的概念. 进程:当程序被执行时的过程可以理解为讲程序从外存调入内存的过程,会为每一个程序 ...
- java oop第14章_Swing(Java界面设计)
一. Swing相关的概念: 1. GUI:(Graphical User Interface):图形化用户界面,通过图形化的方式提供与用户交互的平台,向用户展示信息.收集用户提交的数据. 2. ...
- java oop第09章_JDBC02(CRUD操作)
第09章_JDBC02(CRUD操作) CRUD(CREATE . RETIVE . UPDATE . DELETE)增删改查. DAO中会提供一些CRUD操作方法,调用者可以通过调用这些方法完成相应 ...
- Java OOP——第六章 框架集合
1.集合框架包含的主要内容及彼此之间的关系: 图1: 集合框架:是为了表示和操作集合而统一规定的一种统一的标准体系结构. 包含三大块的内容:对外的接口.接口的是实现和对 ...
- 深入理解java虚拟机-第七章
第7章 虚拟机类加载机制 类的加载的时机 加载 Loading, 连接 Linking(验证 Verfiication, 准备Preparation, 解析 Resolution) 初始化 Initi ...
- java oop第15章_Socket网络编程
一. TCP/IP协议(Transmission Control Protocol/Internet Protocol)传输控制协议/Internet协议,是通信领域的基础.核心协议, 其他的协议 ...
- java oop第12章_IO、序列化和反序列化
引言:数据通常通过文件系统保存在外存中,有时需要将他们读取到程序中进行一些操作,Java针对文件系统的操作提供了一套规范,即IO,针对计算机内存而言,输入的称为输入流,输出的称为输出流. 一. ...
- java oop第11章_反射、BaseDao的进一步改造
引言:从Java5开始,Java中引用了一个新的概念反射,当程序运行时,能动态感知到程序中拥有的所以信息,这个获取信息的过程是采用反射机制来完成. 一. Class类: Class类用 ...
随机推荐
- 【转】浅谈https\ssl\数字证书
转载请注明出处:http://www.cnblogs.com/P_Chou/archive/2010/12/27/https-ssl-certification.html 全球可信的SSL数字证书申请 ...
- Javascript 学习 Boolean
构造函数 new Boolean(value) //构造函数 Boolean(value) //转换函数 参数 value 由布尔对象存放的值或者要转换成布尔值的值 返回值 当作为一个构造函数(带有运 ...
- System.Net.Mail
System.Net.Mail命名空间包含用于将电子邮件发送到简单邮件传输协议(SMTP)服务器进行传送的类. 在此命名空间中,有两个很重要的类: MailMessage 表示可以使用SmtpCli ...
- vue.js ------ 大牛和网站
hellogirl前端网站 : http://www.jqhtml.com/category/article FungLeo: http://blog.csdn.net/FungLeo/article ...
- css 伪元素选择器
/*设置第一个首字母的样式*/ p:first-letter{ color: red; font-size: 30px; } /* 在....之前 添加内容 这个属性使用不是很频繁 了解 使用此伪元素 ...
- [持续更新] Linux基础的重要命令
命令总结:100个左右 mkdir 方法一 [root@localhost ~]# mkdir /test && ls -ld /test 方法二 [root@localhost ~] ...
- jquery的html()、text()、val()的区别和用法
1.html() html()[无参]的使用方法是获取某元素内部的HTML代码,包括各种标签: 例:句1:<p>不知道大家高考的理综试卷里有没有关于科学家及其成就的选择题</p> ...
- [c/c++]判断一个字符串是不是UTF-8字符串
#define CHECK_LENGTH 20 //检查是否为utf8编码时所检查的字符长度 int is_utf8_string(char *utf) { int length = s ...
- GitLab-Runner 安装配置
https://docs.gitlab.com/runner/install/linux-repository.html 直接看官方教程 systemctl status gitlab-runner. ...
- Selenium入门系列5 默认不显示的下拉列表元素操作
本节课程的下拉框是那种默认隐藏,当鼠标移到菜单上下拉框才显示的.如果直接getelement会报错,提示元素不可见: so,得先让下拉列表显示出来再获取元素 用到的新知识: is_display() ...