[Java核心技术] Java多线程
目录
Java 核心技术读书笔记——Java多线程
1 多进程和多线程简介
简介
- 当前的操作系统都是多任务OS
- 每个独立执行的任务就是一个进程
- OS将时间划分为多个时间片(时间很短)
- 每个时间片内将CPU分配给每一个任务,时间片结束,CPU将自动回收,再分配给另外的任务
1.1 多进程优点
- 可以同时运行多个任务
- 程序因为IO堵塞时,可以释放CPU,让CPU为其他程序服务
- 当系统有多个CPU时,可以为多个进程同时服务
- 当前CPU不再提高频率,而是提高核数
1.2 多进程缺点
- 太笨重,不好管理
- 太笨重,不好切换
1.3 多线程概念
- 一个程序可以包括多个子任务,可串/并行
- 每个子任务可以称为一个线程
- 如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作。这样CPU还是保留在本程序中,而不是调度到别的程序(进程)去。提高本程序所获得CPU时间和利用率。
1.4 多进程VS多线程
- 线程共享数据
- 线程通讯更高效
- 线程更轻量级,更容易切换
- 多个线程更容易管理
- 多进程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
public class ProcessDemo1 { public static void main(String[] args) { while(true) { int a = (int) (Math.random() * 100); System.out.println(" main thread is running " + a); try { Thread.sleep(5000); //5000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
- 多线程实例
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
public class ThreadDemo1 { public static void main(String args[]) throws Exception { new TestThread1().start(); while(true) { System.out.println("main thread is running"); Thread.sleep(1000); } } } class TestThread1 extends Thread { public void run() { while(true) { System.out.println(" TestThread1 is running"); try { Thread.sleep(1000); //1000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
2 Java多线程实现
2.1 多线程创建
方法一 java.lang.Thread
- 线程继承
Thread
类,实现run
方法1 2 3 4 5 6 7 8 9 10
public class Thread1 extends Thread{ public void run() { System.out.println("hello"); } public static void main(String[] a) { new Thread1().start(); } }
方法二 java.lang.Runnable
接口
- 线程实现
Runnable
接口,实现run
方法1 2 3 4 5 6 7 8 9 10
public class Thread2 implements Runnable{ public void run() { System.out.println("hello"); } public static void main(String[] a) { new Thread(new Thread2()).start(); } }
Thread
vs Runnable
Thread
占据了父类名额,不如Runnable
方便Thread
类实现Runnable
Runnable
启动时需要Thread
类的支持Runnable
更容易实现多线程中的资源共享- 结论:建议实现
Runnable
接口来完成多线程
2.2 启动
-
start
方法,会自动以新进程调用run
方法,将会是并行运行(多线程运行)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
public class ThreadDemo1 { public static void main(String args[]) throws Exception { new TestThread1().start(); while(true) { System.out.println("main thread is running"); Thread.sleep(1000); } } } class TestThread1 extends Thread { public void run() { while(true) { System.out.println(" TestThread1 is running"); try { Thread.sleep(1000); //1000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * * TestThread1 is running * main thread is running * TestThread1 is running * main thread is running * main thread is running * TestThread1 is running * ... * */
-
直接调用
run
方法,将变成串行执行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
public class ThreadDemo0 { public static void main(String args[]) throws Exception { new TestThread0().run(); while(true) { System.out.println("main thread is running"); Thread.sleep(10); } } } class TestThread0 { public void run() { while(true) { System.out.println(" TestThread1 is running"); try { Thread.sleep(1000); //1000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * TestThread1 is running * TestThread1 is running * TestThread1 is running * ... * */
-
同一个线程,多次
start
会报错,只执行第一次start
方法,多个线程启动,其启动的先后顺序是随机的,由JVM/操作系统来主导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
public class ThreadDemo4 { public static void main(String [] args) { TestThread4 t=new TestThread4(); t.start(); //t.start(); //t.start(); //t.start(); TestThread4 t1=new TestThread4(); t1.start(); } } class TestThread4 extends Thread { public void run() { while(true) { System.out.println(Thread.currentThread().getName() + " is running"); try { Thread.sleep(1000); //1000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * * Thread-1 is running * Thread-0 is running * Thread-1 is running * Thread-0 is running * Thread-1 is running * Thread-1 is running * Thread-0 is running * ... * */
-
线程无需关闭,只要其
run
方法执行结束后,自动关闭 -
main
函数(线程)可能早于新线程结束,整个程序并不终止,整个程序终止是等所有的线程都终止(包括main
函数线程)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
public class ThreadDemo2 { public static void main(String args[]) throws InterruptedException { new TestThread2().start(); } } class TestThread2 extends Thread { public void run() { while(true) { System.out.println("TestThread2 is running"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * TestThread2 is running * TestThread2 is running * TestThread2 is running * ... * */
-
实现
Runnable
的对象必须包装在Thread
类里面,才可以启动,不可以直接对Runnable
的对象进行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
public class ThreadDemo3 { public static void main(String args[]) { //new TestThread3().start(); //Runnable对象必须放在一个Thread类中才能运行 TestThread3 tt= new TestThread3();//创建TestThread类的一个实例 Thread t= new Thread(tt);//创建一个Thread类的实例 t.start();//使线程进入Runnable状态 while(true) { System.out.println("main thread is running"); try { Thread.sleep(1000); //1000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class TestThread3 implements Runnable //extends Thread { //线程的代码段,当执行start()时,线程从此出开始执行 public void run() { while(true) { System.out.println(Thread.currentThread().getName() + " is running"); try { Thread.sleep(1000); //1000毫秒 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
3 Java多线程信息共享
- 粗粒度:子线程与子线程之间、和main线程之间缺乏交流
- 细粒度:线程之间有信息交流通讯
- 通过共享变量达到信息共享
- JDK原生库暂不支持发送消息
3.1 通过共享变量在多个线程中共享消息
static
变量- 同一个
Runaable
类的成员变量
|
|
3.2 多线程信息共享问题
- 工作缓存副本
- 关键步骤缺乏加锁限制
i++
,并非原子性操作- 读取 主存i(正本) 到 工作缓存(副本) 中
- 每个CPU执行 (副本)
i+1
操作 - CPU将结果写入到 缓存(副本) 中
- 数据从 工作缓存(副本) 刷到 主存(正本) 中
变量副本问题解决办法
- 采用
volatile
关键字修饰变量 - 保证不同线程对共享变量操作时的可见性
|
|
关键步骤加锁限制
- 关键步骤加锁限制
- 互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码段
- 同步:多个线程的运行,必须按照某一种规定的先后顺序来运行
- 互斥是同步的一种特例
- 互斥的关键字是
synchronized
synchronized
代码块/函数,只能一个线程进入synchronized
加大性能负担,但是使用简便
|
|
4 Java多线程管理
4.1 线程状态
NEW
刚创建(new)RUNNABLE
就绪态(start)RUNNING
运行中(run)BLOCK
阻塞(sleep)TERMINATED
结束
4.2 线程的方法
Thread
的部分API已经废弃(不建议使用)- 暂停和恢复
suspend/resume
- 消亡
stop/destroy
- 暂停和恢复
线程阻塞/和唤醒
sleep
,时间一到,自己会醒来wait/notify/notifyAll
,等待,需要别人来唤醒join
,等待另外一个线程结束interrupt
,向另外一个线程发送中断信号,该线程收到信号,会触发InterruptedException
(可解除阻塞),并进行下一步处理
|
|
4.3 线程暂停和终止
线程被动暂停和终止
- 依靠别的线程来拯救自己
- 没有及时释放资源
|
|
线程主动暂停和终止
- 定期监测共享变量
- 如果需要暂停或者终止,先释放资源,再主动动作
- 暂停:
Thread.sleep()
,休眠 - 终止:
run
方法结束,线程终止
笔记
interrupted()
是Tread
类的方法,用来测试当前线程是否收到一个INTERRUPT
的信号。收到返回true
,否则返回false
|
|
4.4 多线程死锁
- 每个线程互相持有别人需要的锁(哲学家吃面问题)
- 预防死锁,对资源进行等级排序
笔记
TimeUnit
是 JDK5 引入的新类,位于java.util.concurrent
包中。它提供了单位时间粒度和一些时间转换、计时和延迟等函数
- 线程查看工具
jvisualvm
|
|
4.5 守护(后台)线程
- 普通线程的结束,是
run
方法运行结束 - 守护线程的结束,是
run
方法运行结束或main
函数结束 - 守护线程永远不要访问资源,如文件或数据库等
|
|