1、基本概念:程序、进程、线程
程序、进程、线程
程序:一段静态代码的。 进程:一个正在运行的程序。是一个动态的过程:有自身的产生、存在和消亡的过程。进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。 线程:进程细分为线程,是一个程序内部的一条执行路径。线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(PC),线程切换的开销小。
单核CPU、多核CPU
单核CPU:假多线程,因为在一个时间单元内,只能执行一个线程的任务。 多核CPU:真多线程,同时进行。 一个java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。
并行、并发
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
2、线程的创建和使用(重点)
方式一:继承于Thread类
package com. szu. chen;
import org. junit. Test;
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) ;
}
}
}
}
public class ThreadTest {
@Test
public void test ( ) {
MyThread myThread = new MyThread ( ) ;
myThread. start ( ) ;
myThread. run ( ) ;
System. out. println ( "hello" ) ;
}
}
线程的调度
调度策略:
- 时间片
- 抢占式:高优先级的线程抢占CPU
Java的调度方法
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级,使用优先调度的抢占式策略
线程的优先级等级:
- MAX_PRIORITY: 10
- MIN_PRIORITY: 1
- NORM_PRIORITY: 5
涉及的方法
- getPriority():返回线程优先值
- setProirity(int newPriority):改变线程的优先级
说明:
- 线程创建时继承父线程的优先级
- 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
package com. szu. chen;
class HelloThread extends Thread {
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
if ( i % 2 == 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": " + i + "\t" + getPriority ( ) ) ;
}
if ( i % 20 == 0 ) {
yield ( ) ;
}
}
}
public HelloThread ( String name) {
super ( name) ;
}
}
public class ThreadMethodTest {
public static void main ( String[ ] args) {
HelloThread h1 = new HelloThread ( "线程一" ) ;
h1. setPriority ( Thread. MIN_PRIORITY ) ;
h1. start ( ) ;
Thread. currentThread ( ) . setName ( "主线程" ) ;
Thread. currentThread ( ) . setPriority ( Thread. MAX_PRIORITY ) ;
for ( int i = 0 ; i < 100 ; i++ ) {
if ( i % 2 == 0 ) {
System. out. println (
Thread. currentThread ( ) . getName ( )
+ ": "
+ i
+ "\t"
+ Thread. currentThread ( ) . getPriority ( ) ) ;
}
}
System. out. println ( h1. isAlive ( ) ) ;
}
}
例子:创建三个C窗口卖票,总票数为100张
存在线程安全问题,待解决:同票问题
package com. szu. chen;
class Window extends Thread {
private static int ticket = 100 ;
@Override
public void run ( ) {
while ( true ) {
if ( ticket > 0 ) {
System. out. println ( getName ( ) + ": 卖票,票号为:" + ticket) ;
ticket-- ;
} else {
break ;
}
}
}
}
public class WindowTest {
public static void main ( String[ ] args) {
Window window1 = new Window ( ) ;
Window window2 = new Window ( ) ;
Window window3 = new Window ( ) ;
window1. setName ( "窗口1" ) ;
window2. setName ( "窗口2" ) ;
window3. setName ( "窗口3" ) ;
window1. start ( ) ;
window2. start ( ) ;
window3. start ( ) ;
}
}
方式二:创建多线程的方式二:实现Runnable接口
package com. szu. chen;
class MThread implements Runnable {
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
if ( i % 2 == 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": " + i + "\t" ) ;
}
}
}
}
public class ThreadTest1 {
public static void main ( String[ ] args) {
MThread mThread = new MThread ( ) ;
Thread t1 = new Thread ( mThread) ;
t1. start ( ) ;
}
}
例子:创建三个C窗口卖票,总票数为100张:使用实现Runnable接口的方式
存在线程安全问题,待解决
package com. szu. chen;
class Window1 implements Runnable {
private int ticket = 100 ;
@Override
public void run ( ) {
while ( true ) {
if ( ticket > 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": 卖票,票号为:" + ticket) ;
ticket-- ;
} else {
break ;
}
}
}
}
public class WindowTest1 {
public static void main ( String[ ] args) {
Window1 w = new Window1 ( ) ;
Thread thread1 = new Thread ( w) ;
Thread thread2 = new Thread ( w) ;
Thread thread3 = new Thread ( w) ;
thread1. setName ( "窗口1" ) ;
thread2. setName ( "窗口2" ) ;
thread3. setName ( "窗口3" ) ;
thread1. start ( ) ;
thread2. start ( ) ;
thread3. start ( ) ;
}
}
比较创建线程的两种方法:
开发中:优先选择 实现Runnable接口的方法
原因:1、实现的方式没有类的单继承性的局限性
2、实现的方式更适合来处理多个线程有共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
3、线程的生命周期
4、线程的同步(重点)
1.问题:卖票过程中,出现了重票、错票--》出现了安全问题
2.原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
3.如何解决:当一个线程在操作车票(共享数据)时,其他线程不能参与进来。直到线程a操作完车票时,
其他线程才可以开始操作车票。这种情况即使线程a出现了阻塞,也不能被改变。
4.在java中,通过同步机制来解决线程安全问题
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:1.操作共享数据的代码,即为需要被同步的代码
2. 共享数据:多个线程共同操作的变量。比如:ticket
3. 同步监控器,俗称,锁。任何一个类的对象都可以充当锁
要求:多个线程必须共用同一把锁
package com. szu. chen1;
class Window1 implements Runnable {
private int ticket = 100 ;
private static Object obj = new Object ( ) ;
@Override
public void run ( ) {
while ( true ) {
synchronized ( obj) {
if ( ticket > 0 ) {
try {
Thread. sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": 卖票,票号为:" + ticket) ;
ticket-- ;
} else {
break ;
}
}
}
}
}
public class WindowTest1 {
public static void main ( String[ ] args) {
Window1 w = new Window1 ( ) ;
Thread thread1 = new Thread ( w) ;
Thread thread2 = new Thread ( w) ;
Thread thread3 = new Thread ( w) ;
thread1. setName ( "窗口1" ) ;
thread2. setName ( "窗口2" ) ;
thread3. setName ( "窗口3" ) ;
thread1. start ( ) ;
thread2. start ( ) ;
thread3. start ( ) ;
}
}
package com. szu. chen1;
class Window2 extends Thread {
private static int ticket = 100 ;
private static Object obj = new Object ( ) ;
@Override
public void run ( ) {
while ( true ) {
synchronized ( obj) {
if ( ticket > 0 ) {
try {
Thread. sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": 卖票,票号为:" + ticket) ;
ticket-- ;
} else {
break ;
}
}
}
}
}
public class WindowTest2 {
public static void main ( String[ ] args) {
Window2 w1 = new Window2 ( ) ;
Window2 w2 = new Window2 ( ) ;
Window2 w3 = new Window2 ( ) ;
w1. setName ( "窗口1" ) ;
w2. setName ( "窗口2" ) ;
w3. setName ( "窗口3" ) ;
w1. start ( ) ;
w2. start ( ) ;
w3. start ( ) ;
}
}
4.在java中,通过同步机制来解决线程安全问题
方式一:同步代码块
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
使用同步方法处理实现Runnable类的方法中的线程安全问题
package com. szu. chen1;
class Window3 implements Runnable {
private int ticket = 100 ;
@Override
public void run ( ) {
while ( true ) {
show ( ) ;
}
}
private synchronized void show ( ) {
if ( ticket > 0 ) {
try {
Thread. sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": 卖票,票号为:" + ticket) ;
ticket-- ;
}
}
}
public class WindowTest3 {
public static void main ( String[ ] args) {
Window3 w = new Window3 ( ) ;
Thread thread1 = new Thread ( w) ;
Thread thread2 = new Thread ( w) ;
Thread thread3 = new Thread ( w) ;
thread1. setName ( "窗口1" ) ;
thread2. setName ( "窗口2" ) ;
thread3. setName ( "窗口3" ) ;
thread1. start ( ) ;
thread2. start ( ) ;
thread3. start ( ) ;
}
}
使用同步方法处理继承Thread类的方法中的线程安全问题
package com. szu. chen1;
class Window4 extends Thread {
private static int ticket = 100 ;
@Override
public void run ( ) {
while ( true ) {
show ( ) ;
}
}
private static synchronized void show ( ) {
if ( ticket > 0 ) {
try {
Thread. sleep ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": 卖票,票号为:" + ticket) ;
ticket-- ;
}
}
}
public class WindowTest4 {
public static void main ( String[ ] args) {
Window4 w1 = new Window4 ( ) ;
Window4 w2 = new Window4 ( ) ;
Window4 w3 = new Window4 ( ) ;
w1. setName ( "窗口1" ) ;
w2. setName ( "窗口2" ) ;
w3. setName ( "窗口3" ) ;
w1. start ( ) ;
w2. start ( ) ;
w3. start ( ) ;
}
}
5.同步的方式,解决了线程的安全问题。----好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。---局限
关于同步方法的总结:
- 同步方法仍然涉及到同步监控器,只是不需要我们显式的声明。
- 非静态的同步方法,同步监控器:this,类的对象
- 静态的同步方法,同步监控器是:当前类本身
使用同步机制将单例模式中的懒汉式改写为线程安全的
package com. szu. chen2;
public class BankTest { }
class Bank {
private Bank ( ) { } ;
private static Bank instance = null ;
public static Bank getInstance ( ) {
if ( instance == null ) {
synchronized ( Bank. class ) {
if ( instance == null ) {
instance = new Bank ( ) ;
}
}
}
return instance;
}
}
死锁:
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
- 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
解决方法:
- 专门的算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
package com. szu. chen3;
class A {
public synchronized void foo ( B b) {
System. out. println ( "当前线程名:" + Thread. currentThread ( ) . getName ( ) + "进入了A实例的foo方法" ) ;
try {
Thread. sleep ( 200 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( "当前线程名:" + Thread. currentThread ( ) . getName ( ) + "企图调用B实例的last方法" ) ;
b. last ( ) ;
}
public synchronized void last ( ) {
System. out. println ( "进入了A类的last方法的内部" ) ;
}
}
class B {
public synchronized void bar ( A a) {
System. out. println ( "当前线程名:" + Thread. currentThread ( ) . getName ( ) + "进入了A实例的foo方法" ) ;
try {
Thread. sleep ( 200 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( "当前线程名:" + Thread. currentThread ( ) . getName ( ) + "企图调用B实例的last方法" ) ;
a. last ( ) ;
}
public synchronized void last ( ) {
System. out. println ( "进入了B类的last方法的内部" ) ;
}
}
public class DeadLock implements Runnable {
A a = new A ( ) ;
B b = new B ( ) ;
public void init ( ) {
Thread. currentThread ( ) . setName ( "主线程" ) ;
a. foo ( b) ;
System. out. println ( "进入了主线程之后" ) ;
}
@Override
public void run ( ) {
Thread. currentThread ( ) . setName ( "副线程" ) ;
b. bar ( a) ;
System. out. println ( "进入了副线程之后" ) ;
}
public static void main ( String[ ] args) {
DeadLock d1 = new DeadLock ( ) ;
new Thread ( d1) . start ( ) ;
d1. init ( ) ;
}
}
Lock 锁
1. java.util.concurrent.locks.Lock 接口 是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,
每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
2. ReentrantLock 类 实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用
的是ReentrantLock,可以显式加锁、释放锁。
1.面试题:synchronized 与 Lock 的异同?
相同:解决线程安全
不同:synchronize机制在执行完相应的代码以后,自动的释放同步监视器。·
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
建议:优先使用Lock --> 同步代码块(已经进入了方法体,分配了相应资源) --> 同步方法(在方法体之外)
package com. szu. chen4;
import java. util. concurrent. locks. ReentrantLock;
class Window implements Runnable {
private int Ticket = 100 ;
private ReentrantLock lock = new ReentrantLock ( ) ;
@Override
public void run ( ) {
while ( true ) {
try {
lock. lock ( ) ;
if ( Ticket > 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": 售票,票号为:" + Ticket) ;
Ticket-- ;
} else {
break ;
}
} finally {
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. setName ( "窗口1" ) ;
t2. setName ( "窗口2" ) ;
t3. setName ( "窗口3" ) ;
t1. start ( ) ;
t2. start ( ) ;
t3. start ( ) ;
}
}
多线程练习
题目:银行有一个账号。有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额
分析:
1.是否是多线程问题? 是,两个储户线程
2.是否有共享数据? 有,账户(账户余额)
3.是否有线程安全问题? 有
4.需要考虑如何解决线程安全问题? 同步机制:有三种方式。
package com. szu. exer;
class Account {
private double balance;
public Account ( double balance) {
this . balance = balance;
}
public synchronized void deposit ( double amt) {
if ( amt > 0 ) {
balance += amt;
try {
Thread. sleep ( 100 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( Thread. currentThread ( ) . getName ( ) + "存" + amt + "元成功,当前余额为:" + 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 account = new Account ( 0 ) ;
Customer c1 = new Customer ( account) ;
Customer c2 = new Customer ( account) ;
c1. setName ( "甲储户" ) ;
c2. setName ( "乙储户" ) ;
c1. start ( ) ;
c2. start ( ) ;
}
}
5、线程的通信
线程通信的例子:使用两个线程打印1-100。线程1、线程2 交替打印
线程通信涉及到的三个方法:
wait(): 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify(): 一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就会唤醒优先级高的那个。
notifyAll(): 一旦执行此方法,就会唤醒所有被wait的线程。
说明:
1. wait(),notify(),notifyAll() 三个方法必须使用在同步代码块或同步方法中。
2. wait(),notify(),notifyAll() 三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
即:注意synchronized (this)中的this是默认的同步监视器,可以this.wait()。
但是synchronized (obj)和this.wait(),这个组合使用就会出错。改为obj.wait()
3. wait(),notify(),notifyAll() 三个方法是定义在java.Lang.Object类中。
因为synchronized (obj),括号里obj可以是任意对象,又由要求obj.wait()可知,“任意对象”都需要有wait()方法,那只能是在java.Lang.Object类中。
面试题:sleep() 和 wait() 的异同?
1. 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2. 不同点:1)两个方法声明的位置不同:Thread类中声明sleep(), Object类中声明wait()
2)调用的要求不同:sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步方法中
3)关于是否释放同步监视器:如果两个方法都使用在同步代码或同步方法中,sleep()不会释放锁,wait()会释放锁
package com. szu. chen5;
class Number implements Runnable {
private int number = 1 ;
@Override
public void run ( ) {
while ( true ) {
synchronized ( this ) {
notifyAll ( ) ;
if ( number < 100 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ": " + number) ;
number++ ;
try {
wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
} else {
break ;
}
}
}
}
}
public class CommunicationTest {
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 ( ) ;
}
}
经典例题:生产者/消费者问题
- 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店里没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
- 这里可能出现两个问题:
- 生产者比消费者快时,消费者会漏掉一些数据没有收到。
- 消费者比生产者快时,消费者会取相同的数据。
分析:
1. 是否是多线程问题? 是,生产者线程,消费者线程
2. 是否有共享数据? 是,店员(或产品)
3. 如何解决线程的安全问题? 同步机制,三种方法
4. 是否涉及到线程通信? 是
package com. szu. chen5;
class Clerk {
private int productCount = 0 ;
public synchronized void produceProduct ( ) {
if ( productCount < 20 ) {
productCount++ ;
System. out. println ( Thread. currentThread ( ) . getName ( ) + ":开始生产第" + productCount + "个产品" ) ;
notify ( ) ;
} else {
try {
wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
public synchronized void consumeProduct ( ) {
if ( productCount > 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ":开始消费第" + productCount + "个产品" ) ;
productCount-- ;
notify ( ) ;
} else {
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 ( getName ( ) + ":开始生产产品..." ) ;
while ( true ) {
clerk. produceProduct ( ) ;
}
}
}
class Consumer extends Thread {
private Clerk clerk;
public Consumer ( Clerk clerk) {
this . clerk = clerk;
}
@Override
public void run ( ) {
System. out. println ( getName ( ) + ":开始消费产品..." ) ;
while ( true ) {
clerk. consumeProduct ( ) ;
}
}
}
public class ProductorTest {
public static void main ( String[ ] args) {
Clerk clerk = new Clerk ( ) ;
Producer producer = new Producer ( clerk) ;
producer. setName ( "生产者" ) ;
Consumer consumer = new Consumer ( clerk) ;
consumer. setName ( "消费者" ) ;
producer. start ( ) ;
consumer. start ( ) ;
}
}
6、JDK5.0新增线程创建方式
创建线程的方式三:实现Callable --- JDK 5.0新增
如何理解实现Callable接口创建多线程的方式 比 实现Runnable接口创建多线程的方式强大?
1. call()可以有返回值的。
2. call()可以抛出异常,被外面的操作捕获,获取异常的信息
3. Callable是支持泛型的
package com. szu. chen6;
import java. util. concurrent. Callable;
import java. util. concurrent. ExecutionException;
import java. util. concurrent. FutureTask;
class NumThread implements Callable {
@Override
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 ThreadNew {
public static void main ( String[ ] args) {
NumThread numThread = new NumThread ( ) ;
FutureTask futureTask = new FutureTask ( numThread) ;
new Thread ( futureTask) . start ( ) ;
try {
Object sum = futureTask. get ( ) ;
System. out. println ( "总和为:" + sum) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} catch ( ExecutionException e) {
e. printStackTrace ( ) ;
}
}
}
创建线程的方式四:使用线程池 --- JDK 5.0新增
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
- 好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
package com. szu. chen7;
import java. util. concurrent. * ;
class NumberThread implements Runnable {
@Override
public void run ( ) {
for ( int i = 1 ; i <= 100 ; i++ ) {
if ( i % 2 == 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ":" + i) ;
}
}
}
}
class NumbeThread implements Callable {
@Override
public Object call ( ) throws Exception {
int sum = 0 ;
for ( int i = 1 ; i <= 100 ; i++ ) {
if ( i % 2 != 0 ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) + ":" + i) ;
sum += i;
}
}
return sum;
}
}
public class TreadPool {
public static void main ( String[ ] args) {
ExecutorService executorService = Executors. newFixedThreadPool ( 10 ) ;
ThreadPoolExecutor service = ( ThreadPoolExecutor) executorService;
service. setCorePoolSize ( 15 ) ;
service. setMaximumPoolSize ( 15 ) ;
executorService. execute ( new NumberThread ( ) ) ;
FutureTask futureTask = ( FutureTask) executorService. submit ( new NumbeThread ( ) ) ;
new Thread ( futureTask) . start ( ) ;
Object sum = null ;
try {
sum = futureTask. get ( ) ;
System. out. println ( "总和为:" + sum) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} catch ( ExecutionException e) {
e. printStackTrace ( ) ;
}
executorService. shutdown ( ) ;
}
}
7、额外知识
Thread.star();
启动线程时,都做了什么?
参考:
[Java Thread:揭开Run方法被调用的真正面纱](https://www.linuxidc.com/Linux/2016-03/128997.htm)