目录
4.2.2 同步代码块解决实现Runnable接口中的安全问题
4.2.5 使用同步方法解决实现Runnable接口的线程安全问题
4.2.6 使用同步方法解决继承Thread类的线程安全问题
01、基本概念:程序、进程、线程
- 程序(program):为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
- 进程(process):程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
- 如:运行中的QQ,运行中的MP3播放器程序是静态的,进程是动态的
- 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
- 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
- 若一个进程同一时间并行执行多个线程,就是支持多线程的。
- 线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。
- 一个进程中的多个线程共享相同的内存单元/内存地址空间—》它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
1.1 进程与线程
单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费)。但是因为CPU时间单元特别短,因此感觉不出来。
如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
- 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
- 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
- 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
- 提高计算机系统CPU的利用率
- 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
- 程序需要同时执行两个或多个任务。
- 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
- 需要一些后台运行的程序时。
02 线程的创建和使用
Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。(java支持多线程)
- 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
- 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()
2.1 Thread 类
Thread():创建新的Thread对象
Thread(String threadname):创建线程并指定线程实例名
Thread(Runnabletarget):指定创建线程的目标对象,它实现了Runnable接口中的run方法
Thread(Runnable target, String name):创建新的Thread对象
2.2 API中创建线程的两种方式
2.2.1 方式一 继承Thread类
- 定义子类继承 Thread 类。
- 子类中重写 Thread 类中的 run 方法。
- 创建 Thread 子类对象,即创建了线程对象。
- 调用线程对象 start 方法:启动线程,调用 run 方法 。
class MyThread extends Thread{
//重写Thread类的run(),需要这个线程做的事
@Override
public void run() {
for(int i = 1;i < 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + i); // 获取线程
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3.创建Thread类的子对象
MyThread t1 = new MyThread();
//4.通过此对象调用start():①启动当前线程 ②JVM调用当前线程的run()
t1.start();
//如下操作仍在main线程中执行的
for(int i = 1;i < 100;i++){
if(i % 2 == 0){
System.out.println(i + "***main()***");
}
}
}
}
2.2.2 练习
/**
* 练习:创建两个分线程,其中一个遍历100以内的偶数,另一个遍历100以内的奇数
创建两个Thread子类,分别重写run()
*/
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
m1.start();
MyThread2 m2 = new MyThread2();
m2.start();
}
}
class MyThread 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);
}
}
}
}
/**
* 练习:创建两个分线程,其中一个遍历100以内的偶数,另一个遍历100以内的奇数
*/
public class ThreadDemo {
public static void main(String[] args) {
//创建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();
}
}
2.2.3 Thread类中常用方法
class HelloThread extends Thread{
@Override
public void run() {
for(int i = 0;i < 100; i++){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
// if(i % 20 == 0){
// yield(); //释放当前线程的执行权,但不一定会释放成功
// }
}
}
public HelloThread(String name){
super(name);
}
}
public class ThreadModeTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread("Thread : 1");
// h1.setName("线程一");
h1.start();
//给主线程命名
Thread.currentThread().setName("主线程");
for(int i = 0;i < 100; i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
if(i == 20){
try {
h1.join(); // 线程主线程进入阻塞状态,直到h1执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(h1.isAlive());
}
}
2.2.4 线程的调度
/**
* - 线程的优先级等级
* - MAX_PRIORITY:10
* - MIN _PRIORITY:1
* - NORM_PRIORITY:5 --->默认优先级
* - 涉及的方法
* - getPriority() :返回线程优先值
* - setPriority(intnewPriority) :改变线程的优先级
*
* 说明:高优先级的线程要抢占低优先级线程cpu的执行权。
* 但是只是从概率上讲,高优先级的线程高概率的情况下被执行。
* 并不意味着只有当高优先级的线程执行完以后,低优先级的线程才会被执行。
*/
class HelloThread extends Thread {
@Override
public void run() {
for (int j = 0; j < 100; j++) {
// try {
// sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
if (j % 2 == 0) {
System.out.println(getName() + ":" + getPriority() + ":" + j);
}
}
}
public HelloThread(String name){
super(name);
}
}
public class ThreadModeTest {
public static void main(String[] args) {
HelloThread h2 = new HelloThread("Thread : 1");
h2.start();
//设置分线程的优先级
h2.setPriority(Thread.MAX_PRIORITY);
//给主线程命名
Thread.currentThread().setName("主线程");
Thread.currentThread().setPriority((Thread.MIN_PRIORITY));
for(int j = 0;j < 100; j++){
if(j % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + j);
}
// if(j == 20){
// try {
// h2.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
System.out.println(h2.isAlive());
}
}
/**
* 例子:创建三个c窗口卖票,总票数为100张
*
* 存在线程的安全问题,待解决。
*/
class Windows extends Thread{
private static int ticket = 100; //这里不是静态变量的话,会卖出三百张票,每个线程都会设置100
@Override
public void run() {
while(true){
if(ticket > 0){
System.out.println(getName() + ":卖票,票号为: " + ticket);
ticket--;
}else{
break;
}
}
}
}
public class WindowsTest {
public static void main(String[] args) {
Windows t1 = new Windows();
Windows t2 = new Windows();
Windows t3 = new Windows();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
2.2.5 实现Runnable 接口
//1.创建一个实现了Runnable接口得类
class MThread 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 ThreadTest1 {
public static void main(String[] args) {
//3.创建实现类的对象
MThread m1 = new MThread();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(m1);
//5.通过Thread类的对象调用start():①启动线程 ②调用当前线程的run() --> 调用了Runnable类型的target的run()
t1.start();
//再启动一个线程,遍历100以内的偶数
Thread t2 = new Thread(m1);
t2.setName("线程2");
t2.start();
}
}
2.2.6 两种方式的对比
03 线程的生命周期
04 线程的同步
4.1 理解线程的安全问题
提出问题:
多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
class MThread implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(true){
// 进入阻塞,会提高出现错票的几率
if(tickets > 0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票:票号为" + tickets);
tickets--;
}else{
break;
}
}
}
}
public class ThreadTest1 {
public static void main (String[] args){
// 实现类对象
MThread t1 = new MThread();
// Thread对象,构造器参数为runnable对象
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t1);
Thread thread3 = new Thread(t1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
// 调用start
thread1.start();
thread2.start();
thread3.start();
}
}
当某个线程操作车票尚未完成时,其他线程就参与进来了,会出现错票(0,-1 等)
如何解决: 加锁(类似于排队上厕所)
当一个线程a操作ticketes 时,其他线程不能参与进来,直到线程a操作完成,其他线程才能参与进来,即使a陷入阻塞
4.2 Java中同步机制解决线程安全问题
4.2.1 同步代码块(synchronized()
//方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码(操作共享数据的代码)
}
- 共享数据: 多个线程同时操作的数据\变量 : tickets
- 同步监视器:俗称:锁,任何一个类的对象都可以充当锁
- 要求:多个线程必须公用同一把锁
- 同步的方式,解决了线程的安全问题。---好处 *
- 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。---局限性
4.2.2 同步代码块解决实现Runnable接口中的安全问题
import static java.lang.Thread.sleep;
class MThread implements Runnable{
private int tickets = 100;
Object obj = new Object(); // 共用的锁,保证唯一性
@Override
public void run() {
// Object obj = new Object(); 每个线程进来都会new新的锁,不行
while(true){
synchronized (obj){ // 这里可以考虑用this实现锁,因为只有一个对象
if(tickets > 0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票:票号为" + tickets);
tickets--;
}else{
break;
}
}
}
}
}
public class ThreadTest1 {
public static void main (String[] args){
// 实现类对象
MThread t1 = new MThread();
// Thread对象,构造器参数为runnable对象
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t1);
Thread thread3 = new Thread(t1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
// 调用start
thread1.start();
thread2.start();
thread3.start();
}
}
4.2.3 同步代码块解决继承Thread类中的安全问题
class window extends Thread{
private static int tickets = 100;
private static Object obj = new Object(); // 这里必须是static
@Override
public void run() {
while (true){
synchronized(obj){
if (tickets > 0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " :卖票, 票号为" + tickets);
tickets--;
}else{
break;
}
}
}
}
}
public class WindowTest {
public static void main(String[] args){
window w1 = new window();
window w2 = new window();
window w3 = new window();
w1.setName("窗口一");
w2.setName("窗口二");
w2.setName("窗口三");
w1.start();
w2.start();
w3.start();
}
}
4.2.4 同步方法
4.2.5 使用同步方法解决实现Runnable接口的线程安全问题
class Windows3 implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
show();
}
}
public synchronized void show() { //同步监视器:this
// synchronized (this){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为: " + ticket);
ticket--;
}
// }
}
}
public class WindowsTest3 {
public static void main(String[] args) {
Windows3 w3 = new Windows3();
Thread t1 = new Thread(w3);
Thread t2 = new Thread(w3);
Thread t3 = new Thread(w3);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
4.2.6 使用同步方法解决继承Thread类的线程安全问题
/**
* 使用同步方法处理继承Thread类的方式中的线程安全问题
*/
class Windows4 extends Thread {
private static int ticket = 100;
@Override
public void run() {
while (true) {
show();
}
}
private static synchronized void show(){//同步监视器:Window4.class,当前类
//private synchronized void show(){ //同步监视器:t1,t2,t3。此种解决方式是错误的
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowsTest4 {
public static void main(String[] args) {
Windows4 t1 = new Windows4();
Windows4 t2 = new Windows4();
Windows4 t3 = new Windows4();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
4.2.7 关于同步方法的总结:
* 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
* 2. 非静态的同步方法,同步监视器是:this
* 静态的同步方法,同步监视器是:当前类本身(继承类,一般采用静态数据和静态方法)
4.3 线程安全的单例模式之懒汉式
/**
* 使用同步机制将单例模式中的懒汉式改写为线程安全的
*/
public class BankTest {
}
class Bank{
// 私有化构造器
private Bank(){}
// 内部构造类的实例,且必须静态化
private static Bank instance = null;
// 提供公共方法,返回实例
public static Bank getInstance(){
//方式一:效率稍差
//快捷键:Alt+Shift+Z
// 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;
}
}
05 线程的死锁问题
import static java.lang.Thread.sleep;
public class ThreadTEST {
/*
演示线程死锁问题
*/
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 {
sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (s2) {
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
// 实现Runnabel接口的匿名对象
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2) {
s1.append('c');
s2.append("3");
try {
sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (s1) {
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
/*无sleep输出
ab
12
abcd
1234,也可能Thread2先进行,输出 12,ab,cdab,3412
*/
06解决线程安全问题方法三:lock锁
import java.util.concurrent.locks.ReentrantLock;
/*
解决线程安全方式三: lock锁
在线程中声明一个属性,造一个ReentrantLoc对象
synchronized和lock的异同:
同: 二者都可以解决线程安全问题
不同:
1:lock()需要手动启动同步和结束同步(unlock),而synchronized在执行完相应同步代码后自动释放同同步监视器
2.lock()只有代码块锁,sychronized有方法块锁和方法所
3. lock锁有根本更好的性能
*/
class Window implements Runnable{
private int tickets = 100;
// 1.在线程中声明一个属性,实例化一个ReentrantLoc对象
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
// 2.调用lock()
lock.lock();
if(tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售票, 票号为:" + tickets);
tickets--;
}else{
break;
}
}finally {
// 3. 调用unlock
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args){
Window w = new Window();
Thread t1 = new Thread(w) ;
Thread t2 = new Thread(w) ;
Thread t3 = new Thread(w) ;
t1.start();
t2.start();
t3.start();
}
}
/**
* 银行有一个账户。
* 有两个储户分别向同一个账户存3000元,每次存1000,存3次。
* 每次存完打印账户余额。
*
* 分析:
* 1.是否是多线程问题?是,两个储户线程
* 2.是否有共享数据?有,账户(或账户余额)
* 3.是否有线程安全问题?有
* 4.需要考虑如何解决线程安全问题?同步机制:有三种方式。
*/
class Account{
private double balance;
public Account(double balance){
this.balance = balance;
}
//存钱,同步方法解决继承多线程要慎用
public synchronized void deposit(double amt){
if(amt > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance += amt;
System.out.println(Thread.currentThread().getName() + ":" + "存钱成功,当前余额:" + balance);
}
}
}
class Customer extends Thread{
private Account acct;
public Customer(Account acct){
this.acct = acct;
}
@Override
public void run() {
for(int i = 0;i < 3;i++){
acct.deposit(1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account acct = new Account(0);
Customer c1 = new Customer(acct);
Customer c2 = new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
07 线程的通信
涉及到的三个方法:
* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。o1.wait();
* notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
* notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
*
* 说明:
* 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
* 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常
* 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
/*
使用两个线程打印 1 100 。线程 1, 线程 2 交替打印
*/
class Number extends Thread{
private static int number = 1;
public static Object o1 = new Object();
@Override
public void run() {
while(true){
synchronized (o1){
o1.notify();//唤醒当前线程,如果不加o1,会报错!
if(number <= 100){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "打印:" + number);
number++;
try {
// 使得掉用当前wait的线程进入阻塞状态
o1.wait(); //释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public class ComunicationTest {
public static void main(String[] args){
Number number1 = new Number();
Number number2 = new Number();
number1.setName("线程1");
number2.setName("线程2");
number1.start();
number2.start();
}
}
7.1 sleep()和wait()的异同
7.2 经典例题
/*
1.是否是多线程问题: 是,生产者和消费者线程
2.是否有共享数据:是,店员(clerk),产品
3.如何解决:同步的三种方法
4.通信:wait();notify();是,如果超过20,令线程进入阻塞
*/
class Clerk{
private int productNum = 0;
//生产产品
public synchronized void productProduct() {
if(productNum < 20){
productNum++;
System.out.println(Thread.currentThread().getName() +
"开始生产第 " + productNum + "个产品");
// 使用wait之后就要添加notify,如果不添加,消费者会一直处于阻塞状态
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if(productNum > 0){
System.out.println(Thread.currentThread().getName() + "开始消费第 "
+ productNum + "个产品");
productNum--;
notify();
}else{
//自始至终只造了一个clerk对象,所以锁是共用的
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{ //生产者
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始生产");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.productProduct();
}
}
}
class Customer extends Thread {//消费者
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始消费");
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args){
Clerk clerk = new Clerk();
Producer p1 = new Producer(clerk);
p1.setName("生产者");
Customer c1 = new Customer(clerk);
c1.setName("消费者");
p1.start();
c1.start();
}
}
08 JDK 5.0 新增的创建线程的方式
8.1 实现Callable接口
/*
方式三:实现Callable接口的方式
1.创建实现Callable接口的实现类
2.实现call方法,将此线程需要执行的操作声明在call方法中,同时call需要返回值
3.创建callanle实现类对象
4. 将此实现类对象作为参数传递到FutherTask构造器中,创FutherTask对象
5,将futherTask的对象作为参数传递到Thread类构造器中,并调用Start方法
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1.创建实现Callable接口的实现类
class NumberThread implements Callable {
@Override
//2.实现call方法,将此线程需要执行的操作声明在call方法中,同时call需要返回值
public Object call() throws Exception {
int sum = 0;
for(int i = 1; i <= 100; i++){
if (i % 2 ==0){
System.out.println(i);
sum += i;
}
}
return sum;// 这里进行了装箱
}
}
public class CallableTest {
public static void main(String[] args){
// 3.创建callanle实现类对象
NumberThread numberThread = new NumberThread();
//4. 将此实现类对象作为参数传递到FutherTask构造器中,创FutherTask对象
FutureTask futureTask = new FutureTask(numberThread);//Callable的实现类
// 5,将futherTask的对象作为参数传递到Thread类构造器中,并调用Start方法
new Thread(futureTask).start();
try {
// get()返回值 即为重写的返回值
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
8.2 线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建多线程的方式四:使用线程池
*
* 好处:
* 1.提高响应速度(减少了创建新线程的时间)
* 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
* 3.便于线程管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持多长时间后会终止
*
* 面试题:创建多线程有几种方式?四种!
*/
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //强转
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread()); //适合适用于Runable
service.execute(new NumberThread1()); //适合适用于Runable
// service.submit(Callable callable); //适合适用于Callable
//3.关闭连接池
service.shutdown();
}
}