01、基本概念:程序、进程、线程
- 程序(program):为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
- 进程(process):程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
- 如:运行中的QQ,运行中的MP3播放器程序是静态的,进程是动态的
- 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
- 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
- 若一个进程同一时间并行执行多个线程,就是支持多线程的
- 线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
- 一个进程中的多个线程共享相同的内存单元/内存地址空间—》它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
1.1、进程与线程
单核CPU和多核CPU的理解
- 单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费)。但是因为CPU时间单元特别短,因此感觉不出来。
- 如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
- 一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
并行与并发
- 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
- 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
1.2、使用多线程的优点
背景:
以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?
多线程程序的优点:
- 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
- 提高计算机系统CPU的利用率
- 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
1.3、何时需要多线程
- 程序需要同时执行两个或多个任务。
- 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
- 需要一些后台运行的程序时。
02、线程的创建和使用
2.1、线程的创建和启动
- Java语言的JVM允许程序运行多个线程,它通过
java.lang.Thread
类来体现。 Thread
类的特性- 每个线程都是通过某个特定
Thread
对象的run()
方法来完成操作的,经常把run()
方法的主体称为线程体 - 通过该
Thread
对象的start()方
法来启动这个线程,而非直接调用run()
2.2、Thread类
Thread()
:创建新的Thread对象Thread(String threadname)
:创建线程并指定线程实例名Thread(Runnabletarget)
:指定创建线程的目标对象,它实现了Runnable接口中的run方法Thread(Runnable target, String name)
:创建新的Thread对象
2.3、API中创建线程的两种方式
- JDK1.5之前创建新执行线程有两种方法:
- 继承
Thread
类的方式 - 实现
Runnable
接口的方式
2.3.1、创建多线程的方式一:继承Thread类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
class MyThread extends Thread{ @Override public void run() { for(int i = 1;i < 100;i++){ if(i % 2 == 0){ System.out.println(i); } } } }
public class ThreadTest { public static void main(String[] args) { MyThread t1 = new MyThread();
t1.start();
for(int i = 1;i < 100;i++){ if(i % 2 == 0){ System.out.println(i + "***main()***"); } } } }
|
mt子线程的创建和启动过程
2.3.2、创建过程中的两个问题说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class MyThread extends Thread{ @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) { MyThread t1 = new MyThread();
t1.start();
MyThread t2 = new MyThread(); t2.start();
for(int i = 1;i < 100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":" + i + "***main()***"); } } } }
|
2.3.3、练习1
1、写法一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
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); } } } }
|
2、写法二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public class ThreadDemo { public static void main(String[] args) {
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.3.4、Thread类的有关方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
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); }
} }
public HelloThread(String name){ super(name); } }
public class ThreadModeTest { public static void main(String[] args) { HelloThread h1 = new HelloThread("Thread : 1");
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(); } catch (InterruptedException e) { e.printStackTrace(); } } }
System.out.println(h1.isAlive()); } }
|
2.3.5、线程的调度
- 调度策略
- 时间片
- 抢占式:高优先级的线程抢占CPU
- Java的调度方法
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级,使用优先调度的抢占式策略
2.3.6、线程的优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
class HelloThread extends Thread { @Override public void run() { for (int j = 0; j < 100; j++) {
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); }
}
System.out.println(h2.isAlive()); } }
|
2.3.7、练习2
1、多窗口卖票
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
class Windows 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 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.3.8、创建多线程的方式二:实现Runnable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
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); } } } }
public class ThreadTest1 { public static void main(String[] args) { MThread m1 = new MThread(); Thread t1 = new Thread(m1); t1.start();
Thread t2 = new Thread(m1); t2.setName("线程2"); t2.start(); } }
|
2.3.9、继承方式和实现方式的联系与区别
2.3.10、补充:线程的分类
Java中的线程分为两类:一种是守护线程,一种是用户线程。
- 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
- 守护线程是用来服务用户线程的,通过在
start()
方法前调用thread.setDaemon(true)
可以把一个用户线程变成一个守护线程。 - Java垃圾回收就是一个典型的守护线程。
- 若JVM中都是守护线程,当前JVM将退出。
- 形象理解:兔死狗烹,鸟尽弓藏
03、线程的生命周期
JDK中用Thread.State类定义了线程的几种状态
- 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
- 就绪:处于新建状态的线程被
start()
后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源 - 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,
run()
方法定义了线程的操作和功能 - 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
- 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
线程的生命周期
04、线程的同步
1、提出问题:
多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
2、例题:模拟火车站售票程序,开启三个窗口售票。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Windows1 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 WindowsTest1 { public static void main(String[] args) { Windows1 w = new Windows1();
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(); } }
|
3、理想状态
4、极端状态
4.1、同步代码块处理实现Runnable的线程安全问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
|
class Windows1 implements Runnable{
private int ticket = 100;
@Override public void run() { while(true){ synchronized (this) { if (ticket > 0) {
try{ Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + ":卖票,票号为: " + ticket); ticket--; } else { break; } } } } }
public class WindowsTest1 { public static void main(String[] args) { Windows1 w = new Windows1();
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(); } } class Dog{
}
|
分析同步原理
4.2、同步代码块处理继承Thread类的线程安全问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
class Windows extends Thread{
private static int ticket = 100; private static Object obj = new Object();
@Override public void run() { while(true){
synchronized (Windows.class){
if (ticket > 0) {
try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(getName() + ":卖票,票号为: " + ticket); ticket--; } else { break; } } } } }
public class WindowsTest2 { 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(); } }
|
4.3、同步方法处理实现Runnable的线程安全问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
class Windows3 implements Runnable {
private int ticket = 100;
@Override public void run() { while (true) { show(); } }
public synchronized void show() {
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.4、同步方法处理继承Thread类的线程安全问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
class Windows4 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(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.5、线程安全的单例模式之懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
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; } }
|
4.6、死锁的问题
1、例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
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 { Thread.sleep(100); } 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(100); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (s1){ s1.append("d"); s2.append("4");
System.out.println(s1); System.out.println(s2); } } } }).start(); } }
|
2、例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| class A { public synchronized void foo(B b) { System.out.println("当前线程名: " + Thread.currentThread().getName() + " 进入了A实例的foo方法"); try { Thread.sleep(200); } catch (InterruptedException ex) { ex.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() + " 进入了B实例的bar方法"); try { Thread.sleep(200); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("当前线程名: " + Thread.currentThread().getName() + " 企图调用A实例的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("进入了主线程之后"); }
public void run() { Thread.currentThread().setName("副线程"); b.bar(a); System.out.println("进入了副线程之后"); }
public static void main(String[] args) { DeadLock dl = new DeadLock(); new Thread(dl).start(); dl.init(); } }
|
4.7、Lock锁方式解决线程安全问题
java.util.concurrent.locks.Lock
接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock
对象加锁,线程开始访问共享资源之前应先获得Lock
对象。ReentrantLock
类实现了Lock
,它拥有与synchronized
相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock
,可以显式加锁、释放锁。- 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用
Lock
对象充当。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| import java.util.concurrent.locks.ReentrantLock;
class Windows implements Runnable{
private int ticket = 100; private ReentrantLock lock = new ReentrantLock();
@Override public void run() { while(true){ try{
lock.lock();
if(ticket > 0){
try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + ":售票,票号为: " + ticket); ticket --; }else{ break; } }finally { lock.unlock(); } } } }
public class LockTest { public static void main(String[] args) { Windows w = new Windows();
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(); } }
|
练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
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(); } }
|
05、线程的通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
class Number implements Runnable{
private int number = 1; public Object obj = new Object();
@Override public void run() {
while (true){ synchronized (obj) {
obj.notify();
if(number <= 100){
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + ":" + number); number++;
try { obj.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(); } }
|
5.1、sleep()和wait()的异同
5.2、经典例题:生产者/消费者问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
|
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){
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
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){
try { Thread.sleep(20); } 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("生产者1");
Consumer c1 = new Consumer(clerk); c1.setName("消费者1"); Consumer c2 = new Consumer(clerk); c2.setName("消费者2");
p1.start(); c1.start(); c2.start(); } }
|
06、JDK5.0新增线程创建方式
6.1、创建多线程的方式三:实现Callable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| 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(); } } }
|
Future
接口
- 可以对具体
Runnable、Callable
任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask
是Futrue
接口的唯一的实现类FutureTask
同时实现了Runnable, Future
接口。它既可以作为Runnable
被线程执行,又可以作为Future
得到Callable
的返回值
6.2、使用线程池的好处
1、背景:
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
2、思路:
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
3、好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
corePoolSize
:核心池的大小maximumPoolSize
:最大线程数keepAliveTime
:线程没有任务时最多保持多长时间后会终止- …
6.3、创建多线程的方式四:使用线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor;
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) {
ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
service.execute(new NumberThread()); service.execute(new NumberThread1());
service.shutdown(); } }
|