多线程
一、基本概念
1.程序、进程、线程
程序(program): 是为完成特定任知、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process): 是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程,有它自身的产生、存在和消亡的过程。——生命周期
如:运行中的QQ,运行中的MP3播放器
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread): 进程可进一步细化为线程,是一个程序内部的一条执行路径。
1.若一个进程同一时间并行执行多个线程,就是支持多线程的
2.线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
3.一个进程中的多个线程共享相同的内存单元/内存地址空间→它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
2.并行和串行
并行:多个CPU同时执行多个任务
**并发:**一个CPU同时执行多个任务
3.多线程的优点
1.提高应用程序的响应,对图形界面化更有意义,可增强用户体验。
2.提高计算机系统CPU的利用率。
3.改善程序结构,将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。
4.什么时候创建多线程
1.程序需要同时执行两个或者多个任务。
2.程序需要实现一些需要等待的任务时,如 用户输入,文件读写操作、网络操作、搜索等。
3.需要后台运行一些程序时。
二、线程的创建和使用
1.线程的创建方式一
/*
创建步骤:
1.创建一个继承Thread类的子类
2.重写Thread类的run方法-->将需要的操作写入
3.创建子类的对象
4.通过此对象调用start()
*/
package Thread;
/*
多线程的创建:
*/
//例:遍历100以内的所有偶数
//1.创建一个继承Thread类的子类
class Mythread extends Thread {
// 2.重写Thread类的run方法-->将需要的操作写入
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
//Thread.currentThread ().getName ()获取调用线程的名字
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3.创建子类的对象
Mythread test=new Mythread ();
//4.通过此对象调用start()——①启动当前线程 ②调用当前线程的run
test.start ();
//问题一:test.run (); 这里调用的是主线程main的,应该算是方法
// test.run ();
//问题二:再启动一个线程,不可以让已经启动的线程再执行
// 错误问题二写法 test.run ();
//正确问题二写法:——重新创建线程对象
Mythread test2=new Mythread ();
test2.start ();
//两个线程的运行
//以下操作仍然是在main主线程里的
for(int i=0;i<100;i++) {
if (i % 2!= 0) {
//Thread.currentThread ().getName ()获取调用线程的名字
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}
2.练习_输出偶数
package Thread;
//练习:用两个线程分别输出0-100以内的偶数、奇数
//创建Thread匿名子类——简便方法
public class TTest {
public static void main(String[] args) {
//写法一:创建两个对象,来调用
// MyThread1 thread1=new MyThread1 ();
// MyThread2 thread2=new MyThread2 ();
// thread1.start ();
// thread2.start ();
//写法二:创建Thread匿名子类
new Thread (){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}.start ();
new Thread (){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2!=0){
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}.start ();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2!=0){
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}
3.线程的常用方法
/*测试Thread中的常用方法
1. start():启动当前线程;调用当前线程的run()
2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3. currentThread():静态方法,返回执行当前代码的线程
4. getName()∶获取当前线程的名字
5. setName():设置当前线程的名字
也可以通过构造器进行改名
6.yield(): 释放当前cpu的执行权——>下一个被谁抢去,不一定
7.join(): 在线程A中调用线程B的join(),此时A进入阻塞状态,只有B完全执行完之后,A才会执行
8.stop(): 已过时,强制结束
9.sleep(): 让当前进程“休眠”指定的毫秒数,单位为毫秒,
10.isAlive(): 判断当前进程是否存活
*/
package Thread;
class Mythread4 extends Thread {
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
//3.Thread.currentThread ().getName ()获取调用线程的名字
System.out.println (Thread.currentThread ().getName ()+":"+i);
//sleep()——休眠
if(i==60){
System.out.println ("**************");
try {
sleep (1000);
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
Mythread4 mythread4=new Mythread4 ();
Thread.currentThread ().setName ("主线程:");
mythread4.start ();
for(int i=0;i<100;i++){
if(i%2!=0){
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
if(i==20){
System.out.println ("————————");
try {
//yield()——释放当前CPU执行权
mythread4.yield ();
//join()——换线程
mythread4.join();
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
}
}
4.线程优先级的设置
/*
线程的优先级:
1.MAX_PRIORITY: 10
2.MIN_PRIORITY: 1
3.NORM_PRIORITY: 5-->默认的优先级
如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p)∶设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
*/
package Thread;
class Mythread4 extends Thread {
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println (Thread.currentThread ().getName ()+":"+Thread.currentThread ().getPriority ()+":"+i);//获取分线程的优先级
}
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
Mythread4 mythread4=new Mythread4 ();
Thread.currentThread ().setName ("主线程:");
Thread.currentThread ().setPriority (9); //设置主线程额优先级为10
mythread4.start ();
for(int i=0;i<100;i++){
if(i%2!=0){
System.out.println (Thread.currentThread ().getName ()+":"+Thread.currentThread ().getPriority ()+":"+i);
}
}
}
}
5.例题一
多窗口卖票_使用继承Thread方式(存在安全性问题)
package Thread;
/*
* 例题:创建三个窗口卖票,总票数为100
* 注意:1.这里窗口1,2,3都卖了100这张票——多线程的安全性问题没解决
* 2.这里的票属于三个窗口共同卖的,所以,应该是static静态的。
*/
class Windows extends Thread{
//卖票系统
private static int ticket=100;
private static Object object=new Object ();
@Override
public void run() {
while (true){
//使用同步代码块的方式解决安全问题:
//错误写法:synchronized (this){
//因为this包含三个对象w1,w2,w3
synchronized (object){
if(ticket>0){
try {
Thread.sleep (100);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName ()+":票号为:"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Windows w1=new Windows ();
Windows w2=new Windows ();
Windows w3=new Windows ();
w1.setName ("窗口1");
w2.setName ("窗口2");
w3.setName ("窗口3");
w1.start ();
w2.start ();
w3.start ();
}
}
***6.线程创建方式二(实现Runnable接口)
/*
* 创建多线程的方式二:实现Runnable接口
1.创建一个实现了Runnable接口的类
2.实现类去实现Runnable中的抽象方法: run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()
*/
package Thread;
//1.创建一个实现了Runnable接口的类
class Mythread5 implements Runnable{
//2.实现类去实现Runnable中的抽象方法: run()
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
System.out.println (Thread.currentThread ().getName ()+":"+i);
}
}
}
}
public class Thread2 {
public static void main(String[] args) {
//3.创建实现类的对象
Mythread5 mythread5=new Mythread5 ();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1= new Thread (mythread5);
//5.通过Thread类的对象调用start()
t1.setName ("线程1");
t1.start ();
Thread t2= new Thread (mythread5);
t2.setName ("线程2");
t2.start ();
}
}
7.例题二:
多窗口卖票_使用接口Runnable的方式
package Thread;
class Mythread9 implements Runnable{
public int ticket=100;
//创建同步监视器的锁
Object object=new Object ();
@Override
public void run() {
while(true){
//同步代码块的方式解决安全问题
synchronized(object){ //写法一:
//写法二: synchronized(this){
//写法三: synchronized(Mythread9.class){
if(ticket>0){
try {
Thread.sleep (1000);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName ()+":票号为:"+ticket);
ticket--;
}else
{
break;
}
}
}
}
}
public class ThraedTest2 {
public static void main(String[] args) {
Mythread9 mythread9=new Mythread9 ();
Thread t1 = new Thread (mythread9);
t1.setName ("窗口1");
t1.start ();
Thread t2 = new Thread (mythread9);
t2.setName ("窗口2");
t2.start ();
Thread t3= new Thread (mythread9);
t3.setName ("窗口3");
t3.start ();
}
}
8.两种创建方式的对比
开发中:优先选择:实现Runnable接口的方式原因:
1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。
联系: public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
三、线程的生命周期
线程的几种状态
**新建:**当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态 ,run()方法定义了线程的操作和功能
**阻塞:**在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中 止自己的执行,进入阻塞状态
**死亡:**线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDm5psVB-1637993243871)(C:\Users\86173\AppData\Roaming\Typora\typora-user-images\image-20211125182333374.png)]
四、线程的同步
1.线程的安全问题
问题描述:
1.问题:卖票过程中,出现了重票、错票–>出现了线程的安全问题
2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票售卖
3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程α操作完ticket,其他线程才可以开始操作ticket。这种情况即使线程α出现了阻塞,也不能被改变。
4.在Java中,我们通过同步机制,来解决线程的安全问题。
a.同步代码块
/*方法一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:1.操作共享数据的代码,即为需要被同步的代书
2.共享数据:多个线程共同操作的变量。比如: ticket就是共享数据。
3.同步监视器:俗称:锁。任何一个类的对象,都可以当成锁。
要求:多个线程必须要共用同一把锁。
补充:可以 考虑 使用this充当锁
*/
b.同步方法
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
//继承Thread的方法示例:
class Windows extends Thread{
//卖票系统
private static int ticket=100;
@Override
public void run() {
show ();
}
//使用同步方法的方式解决线程的安全性问题
public static synchronized void show(){ //同步监视器:Windows.class
// 错误写法: public static synchronized void show(){ //同步监视器:t1,t2,t3
while (true) {
if(ticket>0) {
try {
Thread.sleep (10);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName () + ":票号为:" + ticket);
ticket--;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Windows w1=new Windows ();
Windows w2=new Windows ();
Windows w3=new Windows ();
w1.setName ("窗口1");
w2.setName ("窗口2");
w3.setName ("窗口3");
w1.start ();
w2.start ();
w3.start ();
}
}
//继承Runnable接口的安全性解决
class Mythread9 implements Runnable{
public int ticket=100;
//创建同步监视器的锁
@Override
public void run() {
while(true) {
show ();
}
}
public synchronized void show(){
if(ticket>0){
try {
Thread.sleep (90);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName ()+":票号为:"+ticket);
ticket--;
}
}
}
public class ThraedTest2 {
public static void main(String[] args) {
Mythread9 mythread9=new Mythread9 ();
Thread t1 = new Thread (mythread9);
t1.setName ("窗口1");
t1.start ();
Thread t2 = new Thread (mythread9);
t2.setName ("窗口2");
t2.start ();
Thread t3= new Thread (mythread9);
t3.setName ("窗口3");
t3.start ();
}
}
c. LOCK锁
//具体步骤:
//1.实例化ReentrantLock
private ReentrantLock lock=new ReentrantLock ();
try{
//2.调用锁定lock方法
lock.lock ();
//需要锁的代码
}finally{
//3.调用解锁方法
lock.unlock ();
}
import java.util.concurrent.locks.ReentrantLock;
class Mythread9 implements Runnable{
public int ticket=100;
//1.实例化ReentrantLock
private ReentrantLock lock=new ReentrantLock ();
@Override
public void run() {
while(true) {
try{
//2.调用锁定lock方法
lock.lock ();
if(ticket>0){
try {
Thread.sleep (90);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName ()+":票号为:"+ticket);
ticket--;
}else{
break;
}
}finally {
//3.调用解锁方法
lock.unlock ();
}
}
}
}
public class ThraedTest2 {
public static void main(String[] args) {
Mythread9 mythread9=new Mythread9 ();
Thread t1 = new Thread (mythread9);
t1.setName ("窗口1");
t1.start ();
Thread t2 = new Thread (mythread9);
t2.setName ("窗口2");
t2.start ();
Thread t3= new Thread (mythread9);
t3.setName ("窗口3");
t3.start ();
}
}
2.面试题: synchronized 与Lock的异同?
相同: 二者都可以解决线程安全问题
不同: synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(Lock())),同时结束同步也需要手动的实现(unlock()
优先使用顺序:
Lock→同步代码块(已经进入了方法体,分配了相应资源)→同步方法(在方法体之外)
3.线程安全的单例模式之懒汉式
//线程安全的单例模式之懒汉式
public class AAAA {
public static void main(String[] args) {
}
}
class Bank{
private Bank(){ }
private static Bank instance=null;
private synchronized static Bank getInstance() {
//方式一:效率差
// synchronized (Bank.class){
// if(instance==null){
// instance=new Bank ();
// }
// return instance;
//
// }
//方式二:
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank ();
}
}
}
return instance;
}
}
4.死锁的问题
死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
//死锁示例代码:
public class DiedThread {
public static void main(String[] args) {
StringBuffer s1=new StringBuffer ();
StringBuffer s2=new StringBuffer ();
new Thread (){
@Override
public void run() {
synchronized (s1){
s1.append ("a");
s2.append (1);
// try {
// Thread.sleep (99);
// } catch (InterruptedException e) {
// e.printStackTrace ();
// }
synchronized (s2){
s1.append ("b") ;
s2.append (2);
System.out.println (s1);
System.out.println (s2);
}
}
}
}.start ();
new Thread (new Runnable () {
@Override
public void run() {
synchronized (s2){
s1.append ("c");
s2.append (3);
// try {
// Thread.sleep (99);
// } catch (InterruptedException e) {
// e.printStackTrace ();
// }
synchronized (s1){
s1.append ("d") ;
s2.append (4);
System.out.println (s1);
System.out.println (s2);
}
}
}
}){
}.start ();
}
}
5.银行例题:
package Test;
/*
银行有一个账户。
有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
分析:
1.是否是多线程问题?是,两个储户线程
2.是否有共享数据?有,账户(或账户余额)
3.是否有线程安全问题﹖有
4.需要考虑如何解决线程安全问题?
同步机制:有三种方式。
*/
import java.util.concurrent.locks.ReentrantLock;
class Account{
private double balance=0;
public Account(double balance) {
this.balance = balance;
}
public Account() {
}
//存钱的方法
//解决线程安全性方法——lock锁
private ReentrantLock lock=new ReentrantLock();
//解决线程安全性方法——synchronized同步方法
// public void deposit(double amt){
public void deposit(double amt){
try{
lock.lock ();
if(amt>0){
balance=amt+balance;
try {
Thread.sleep (1000);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName ()+"存钱成功!"+"余额为:"+balance);
}
}finally {
lock.unlock ();
}
}
}
class Customer extends Thread{
private Account account;
public Customer (Account account){
this.account=account;
}
@Override
public void run() {
for(int i=0;i<3;i++){
account.deposit (1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account account=new Account ();
Customer c1=new Customer (account);
Customer c2=new Customer (account);
c1.setName ("甲");
c2.setName ("乙");
c1.start ();
c2.start ();
}
}
五、线程的通信
1.线程通信的常用方法
wait(); 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify(); 一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
notifyAll();一旦执行此方法,就会唤醒所有被wait的线程。
注意点:
1.wait(); notify(); notifyAll(); 三个方法必须使用在同步代码块或同步方法中。
2.wait(); notify(); notifyAll(); 三个方法的调用者必须使用在同步代码块或同步方法中的同步监视器,否则会出现异常
2.交互打印例题:
/*
线程通信的例子:
使用两个线程打印1-100。线程1,线程2交替打印
*/
class Number implements Runnable{
private int number=1;
@Override
public void run() {
while(true){
synchronized (this) {
//唤醒进程
notify ();
if(number<=100){
try {
Thread.sleep (10);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println (Thread.currentThread ().getName ()+":"+number);
number++;
try {
//使得调用如下wait()方法的线程进入阻塞状态
wait ();
} catch (InterruptedException e) {
e.printStackTrace ();
}
}else{
break;
}
}
}
}
}
public class TongTest {
public static void main(String[] args) {
Number number=new Number ();
Thread t1 = new Thread (number);
Thread t2 = new Thread (number);
t1.setName ("线程1 ");
t2.setName ("线程2 ");
t1.start ();
t2.start ();
}
}
3. sleep和wait的区别
1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
2.不同点:
a.两个方法声明的位置不同:
Thread类中声明sLeep() , object类中声明wait()
b.调用的要求不同:
sLeep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步方法里
c.关于是否释放同步监视器:
如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁
4.生产者/消费者例题
package Test;
/*
线程通信的应用:
经典例题:生产者/消费者问题
生产者(Productor)将产品交给店员(CLerk),而消费者(Customer)从店员处取走产品,
店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,
如果店中有空位放产品了再通知生产者继续生产;
如果店中没有产品了,店员会告诉消费者等一下,
如果店中有产品了再通知消费者来取走产品。
分析:
1.是否是多线程问题?是,生产者线程,消费者线程
2.是否有共享数据?是,店员(或产品)
3.如何解决线程的安全问题﹖同步机制,有三种方法
4.是否涉及线程的通信?是
*/
class Cleck{//店员
private int productioncount=0;//产品数量为0
public synchronized void productthing() {//生产产品的方法
if(productioncount<20){
productioncount++;
System.out.println (Thread.currentThread ().getName ()+":生产第"+productioncount+"个产品了");
notify ();
}else{
//等待
try {
wait ();
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
public synchronized void customerthing() {//消费产品的方法
if(productioncount>0){
System.out.println (Thread.currentThread ().getName ()+":消费第"+productioncount+"个产品了");
productioncount--;
notify ();
}else{
//等待
try {
wait ();
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
}
class Producer extends Thread{//生产者
private Cleck cleck;
public Producer(Cleck cleck) {
this.cleck = cleck;
}
@Override
public void run() {
while(true){
try {
Thread.sleep (100);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println ("开始生产产品啦........");
cleck.productthing();
}
}
}
class Customerr extends Thread{//消费者
private Cleck cleck;
public Customerr(Cleck cleck) {
this.cleck = cleck;
}
@Override
public void run() {
while(true){
try {
Thread.sleep (120);
} catch (InterruptedException e) {
e.printStackTrace ();
}
System.out.println ("开始消费产品啦........");
cleck.customerthing();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Cleck cleck=new Cleck ();
Producer p1=new Producer (cleck);
Customerr c1=new Customerr (cleck);
p1.setName ("生产者1");
c1.setName ("消费者1");
p1.start ();
c1.start ();
}
}
六、新增线程创建方式
1.线程的创建方式三:实现Callable接口
//创建步骤为:
//1.创建一个实现CalLable的实现类
//2.实现calL方法,将此线程需要执行的操作声明在call()中
//3.创建callabLe接口实现类的对象
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
//6.获取Callable中call方法的返回值
package Test;
/*
线程的创建方式三:实现Callable接口
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1.创建一个实现CalLable的实现类
class NumThread implements Callable {
//2.实现calL方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum=0;
for(int i=1;i<=100;i++){
if(i%2==0){
sum+=i;
}
}
return sum;
}
}
public class ThreadNew1 {
public static void main(String[] args) {
//3.创建callabLe接口实现类的对象
NumThread numThread=new NumThread ();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask (numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread (futureTask).start ();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造参数Callable实现类重写的call()的返回值
Object sum = futureTask.get ();
System.out.println ("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace ();
} catch (ExecutionException e) {
e.printStackTrace ();
}
}
}
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
- call()可以有返回值的。
- call()可以抛出异常,被外面的操作捕获,获取异常的信息
- callable是支持泛型的
2.线程的创建方式四:使用线程池
a.使用线程池的优点:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
…
b.创建步骤及例子
//步骤:
//1.提供指定线程数量的线程池
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
//3.关闭连接池
package Test;
/*
线程的创建方式四:使用线程池
*/
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <=10 ; i++) {
if(i%2==0){
System.out.println (Thread.currentThread ().getName ()+" :"+i);
}
}
}
}
class NumberThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i <=10 ; i++) {
if(i%2!=0){
System.out.println (Thread.currentThread ().getName ()+" :"+i);
}
}
}
}
public class ThreadNew2 {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool (10);
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute (new NumberThread ());//适用于Runnable
service.execute (new NumberThread2 ());
// service.submit (Callable callable);//适合使用于Callable
//3.关闭连接池
service.shutdown ();
}
}
1.提供指定线程数量的线程池
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
//3.关闭连接池
package Test;
/*
线程的创建方式四:使用线程池
*/
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <=10 ; i++) {
if(i%2==0){
System.out.println (Thread.currentThread ().getName ()+" :"+i);
}
}
}
}
class NumberThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i <=10 ; i++) {
if(i%2!=0){
System.out.println (Thread.currentThread ().getName ()+" :"+i);
}
}
}
}
public class ThreadNew2 {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool (10);
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute (new NumberThread ());//适用于Runnable
service.execute (new NumberThread2 ());
// service.submit (Callable callable);//适合使用于Callable
//3.关闭连接池
service.shutdown ();
}
}