JavaSe——线程
2015-03-03 17:49
45 查看
A.线程的概述和多线程的意义
l 多线程概述• 进程:
• 正在运行的程序,是系统进行资源分配和调用的独立单位。
• 每一个进程都有它自己的内存空间和系统资源。
• 线程:
• 是进程中的单个顺序控制流,是一条执行路径
• 一个进程如果只有一条执行路径,则称为单线程程序。
• 一个进程如果有多条执行路径,则称为多线程程序。
1:要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在。
2:什么是进程?
通过任务管理器我们就看到了进程的存在。
而通过观察,我们发现只有运行的程序才会出现进程。
进程:就是正在运行的程序。
进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
3:多进程有什么意义呢?
单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。
举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。
也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。
并且呢,可以提高CPU的使用率。
问题:
一边玩游戏,一边听音乐是同时进行的吗?
不是。因为单CPU在某一个时间点上只能做一件事情。
而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。
4:什么是线程呢?
在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
单线程:如果程序只有一条执行路径。
多线程:如果程序有多条执行路径。
5:多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
6.并行和并发
并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。
并发:物理上同时发生,指在某一个时间点同时运行多个程序。
B.Java程序运行原理
l Java程序运行原理• java 命令会启动java 虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程” ,然后主线程去调用某个类的 main 方法。所以main方法运行在主线程中。在此之前的所有程序都是单线程的。
• 思考:
• jvm虚拟机的启动是单线程的还是多线程的?
多线程。原因是垃圾回收线程也要先启动,否则很容易出现内存溢出。
现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。
C.如何实现多线程及多线程方式1的思路
方式1:step1.写一个MyThread类继承Thread类,
step2.重写MyThread里面的run方法
step3.创建MyThread对象
step4.启动线程
问题:为什么要重写run方法呢?
因为不是类中所有的代码都需要被线程执行的。
而为了区分哪些代码被执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
package com.core.thread; public class MyThread extends Thread { public MyThread() { super(); } public MyThread(String name) { super(name); } @Override public void run() { //耗时操作 for (int i = 0; i < 10; i++) { //获取线程的名称 System.out.println(getName()+"::"+i); } } }
package com.core.thread; public class Test { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); // 设置线程名称 t2.setName("线程t2"); Thread t3 = new MyThread("线程t3"); // 获得main函数的线程名称? // 返回当前正在执行的线程对象 Thread currentThread = Thread.currentThread(); System.out.println("currentThread::" + currentThread.getName()); } }
D.线程调度及获取和设置线程优先级
package com.core.thread; public class Test { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); // 设置线程名称 t2.setName("线程t2"); Thread t3 = new MyThread("线程t3"); // 获得main函数的线程名称? // 返回当前正在执行的线程对象 Thread currentThread = Thread.currentThread(); System.out.println("currentThread::" + currentThread.getName()); // • public final int getPriority() // • public final void setPriority(int newPriority) System.out.println(t1.getPriority()); System.out.println(t2.getPriority()); System.out.println(t3.getPriority()); t1.setPriority(10); t2.setPriority(1); t3.setPriority(1); t1.start(); t2.start(); t3.start(); } }
E.休眠线程
• 线程休眠• public static void sleep(long millis)
package com.core.thread.demo1; import java.util.Date; public class SleepThread extends Thread { public SleepThread() { super(); } public SleepThread(String name) { super(name); } @Override public void run() { //耗时操作 for (int i = 0; i < 100; i++) { //获取线程的名称 System.out.println(getName()+"--"+i+","+new Date()); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
测试类:
package com.core.thread.demo1; public class Test { public static void main(String[] args) { SleepThread st1 = new SleepThread(); SleepThread st2 = new SleepThread("线程2"); st1.setName("线程1"); st1.start(); st2.start(); } }
F.加入线程
• 线程加入• public final void join()
等待线程结束
package com.core.thread.demo1; import java.util.Date; public class JoinThread extends Thread { public JoinThread() { super(); } public JoinThread(String name) { super(name); } @Override public void run() { //耗时操作 for (int i = 0; i < 100; i++) { //获取线程的名称 System.out.println(getName()+"--"+i+","+new Date()); } } }
测试类:
package com.core.thread.demo1; public class Test { // 线程加入 // public final void join() public static void main(String[] args) { JoinThread jt1 = new JoinThread("爸爸"); JoinThread jt2 = new JoinThread("大儿子"); JoinThread jt3 = new JoinThread("小儿子"); jt1.start(); try { jt1.join(); } catch (InterruptedException e) { e.printStackTrace(); } jt2.start(); jt3.start(); } }
G.礼让线程
• 线程礼让• public static void yield()
package com.core.thread.demo3; import java.util.Date; public class YieldThread extends Thread { public YieldThread() { super(); } public YieldThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + "--" + i); Thread.yield(); } } }
测试类:
package com.core.thread.demo3; public class Test { //线程礼让,暂停当前正在执行的线程对象,并执行其他线程 //让多个线程的执行更协调,但是不能保证一人一次 public static void main(String[] args) { YieldThread st1 = new YieldThread(); YieldThread st2 = new YieldThread("线程2"); st1.setName("线程1"); st1.start(); st2.start(); } }
H.守护线程
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
package com.core.thread.demo4; import java.util.Date; public class DaemonThread extends Thread { public DaemonThread() { super(); } public DaemonThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + "--" + i); } } }
测试类:
package com.core.thread.demo4; public class Test { // public final void setDaemon(boolean on) // 将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 // 该方法必须在启动线程前调用 public static void main(String[] args) { DaemonThread dt1 = new DaemonThread(); DaemonThread dt2 = new DaemonThread("关羽"); dt1.setName("张飞"); // 设置dt1和dt2为守护线程,必须放在启动线程之前 dt1.setDaemon(true); dt2.setDaemon(true); dt1.start(); dt2.start(); Thread.currentThread().setName("刘备"); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "," + i); } } }
I .中断线程
package com.core.thread.demo5; import java.util.Date; public class StopThread extends Thread { public StopThread() { super(); } public StopThread(String name) { super(name); } @Override public void run() { long currentTimeMillis = System.currentTimeMillis(); Date date = new Date(currentTimeMillis); System.out.println("开始时间:" + date); try { sleep(5000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("线程被终止"); } currentTimeMillis = System.currentTimeMillis(); date = new Date(currentTimeMillis); System.out.println("结束时间:" + date); } }
测试类:
package com.core.thread.demo5; public class Test { public static void main(String[] args) { StopThread st1 = new StopThread("线程1"); st1.start(); try { Thread.currentThread().sleep(3000); // st1.stop(); st1.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } }
注意在终止线程的时候,stop和interrupt的区别。
J.线程生命周期图解
K.多线程方式2的思路及代码实现
l 实现Runnable接口• 如何获取线程名称
• 如何给线程设置名称
l 实现接口方式的好处
• 可以避免由于Java单继承带来的局限性。
• 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
package com.core.thread.demo6; public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+","+i); } } }
测试类:
package com.core.thread.demo6; public class Test { // 多线程的方式2 // a.写一个MyRunnable类继承Runnable接口 // b.重写run()方法 // c.创建MyRunnable对象 // d.创建Thread对象,并把c步骤创建的MyRunnable对象作为构造参数传递 public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread t1 = new Thread(runnable, "线程1"); Thread t2 = new Thread(runnable, "线程2"); t1.start(); t2.start(); } }
L.多线程两种方式的图解比较及区别
M.线程安全
如,某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。用多线程来实现,两种方法。
第一种,继承Thread类,重写run方法。
第二种,实现Runnable接口。
先用第一种方法:
package com.core.thread.demo7; public class SellTicket extends Thread { public static int ticketNum = 100; public SellTicket(String name){ super(name); } @Override public void run() { // while(ticketNum>0){ // // } while(true){ if(ticketNum >0){ System.out.println(Thread.currentThread().getName()+"正在出售"+ticketNum+"张票"); ticketNum--; } } } }
测试类:
package com.core.thread.demo7; //某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票 public class Test { public static void main(String[] args) { SellTicket st1 = new SellTicket("窗口1"); SellTicket st2 = new SellTicket("窗口2"); st1.start(); st2.start(); } }
通过打印可以看出,这个是存在线程安全的!暂时先放一下,稍后解决。
接下来,使用实现Runnable接口的方式。
package com.core.thread.demo8; public class SellTicket implements Runnable { private static int ticketNum = 100; @Override public void run() { while(true){ if(ticketNum >0){ try { Thread.currentThread().sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售"+ticketNum+"张票"); ticketNum--; } } } }
package com.core.thread.demo8; //某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票 //实现Runnable接口 public class Test { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); t1.start(); t2.start(); } }
注意:为了演示方便,我在售票的时候,睡眠了一段时间。从打印的数据来看,还是存在线程安全的问题。
N.出现了同票和负数票的原因分析
l 问题• 相同的票出现多次
• CPU的一次操作必须是原子性的
• 还出现了负数的票
• 随机性和延迟导致的
l 注意
• 线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
O.线程安全问题的产生原因分析
l 首先想为什么出现问题?(也是我们判断是否有问题的标准)• 是否是多线程环境
• 是否有共享数据
• 是否有多条语句操作共享数据
l 如何解决多线程安全问题呢?
• 基本思想:让程序没有安全问题的环境。
• 怎么实现呢?
• 把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
P.使用同步代码块解决线程安全问题
package com.core.thread.demo8; public class SellTicket implements Runnable { private static int ticketNum = 100; //创建锁对象 private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } } } }
package com.core.thread.demo8; //某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票 //实现Runnable接口 public class Test { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); t1.start(); t2.start(); t3.start(); } }
Q.同步代码块的特点和优缺点
l 同步的前提• 多个线程
• 多个线程使用的是同一个锁对象
l 同步的好处
• 同步的出现解决了多线程的安全问题。
l 同步的弊端
• 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
R.同步方法
package com.core.thread.demo10; public class SellTicket implements Runnable { private static int ticketNum = 100; @Override public void run() { while(true){ sellTicket(); } } private synchronized void sellTicket() { if(ticketNum >0){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售"+ticketNum+"张票"); ticketNum--; } } }
S.关于使用同步方法锁的问题
回顾一下,关于我们使用的锁!package com.core.thread.demo11; public class SellTicket implements Runnable { private static int ticketNum = 100; private Object obj = new Object(); private Test t = new Test(); @Override public void run() { while (true) { // 使用Object类为锁 /* synchronized (obj) { if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } // 使用任意类为锁 synchronized (t) { if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } */ // 使用字节码类为锁 synchronized (SellTicket.this) { if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } } } private synchronized void sellTicket() { if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } }
再回过来,看同步方法到底是使用哪一个锁呢?
package com.core.thread.demo12; public class SellTicket implements Runnable { private static int ticketNum = 100; private Object obj = new Object(); private Test t = new Test(); private int x = 1; @Override public void run() { while (true) { if (x % 2 == 1) { synchronized (this) { // 这里注意了,如果是SellTicke.class 设为锁,则会出现售0票的现象。 // 但是如果要是this,或者SellTicket.this 设为锁,则售票正常[可以看下面的例子,sellTicket为静态方法] if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } } else { sellTicket(); } x++; } } private synchronized void sellTicket() { if (ticketNum > 0) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售" + ticketNum + "张票"); ticketNum--; } } }
静态方法发锁:
package com.core.thread.demo12; //某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票 //实现Runnable接口 public class Test { // 小结: // A:同步代码块上的锁对象是谁? // 任意对象。 // B:同步方法是锁是谁? // this // C:静态方法的锁对象是谁? // 类的字节码文件 public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); t1.start(); t2.start(); t3.start(); } }
小结:
A:同步代码块上的锁对象是谁?
任意对象。
B:同步方法是锁是谁?
this
C:静态方法的锁对象是谁?
类的字节码文件对象
T.回顾以前的线程安全的类
package com.core.thread.demo13; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Vector; public class Test { public static void main(String[] args) { // 回顾以前讲过的线程安全的类 StringBuffer sb = new StringBuffer(); Vector<Object> vector = new Vector<>(); Hashtable<Object, Object> hashtable = new Hashtable<>(); ArrayList<Object> list = new ArrayList<>();// 线程不安全。 // Collections类中给我提供一个方法 // synchronizedList // public static <T> List<T> synchronizedList(List<T> list) // 返回指定列表支持的同步(线程安全的)列表。为了保证按顺序访问,必须通过返回的列表完成所有对底层实现列表的访问。 List<Object> list2 = Collections.synchronizedList(list);//线程安全。 } }
相关文章推荐
- JavaSE第九十八讲:线程的实现方式
- JAVASE总结--线程
- JAVASE----07----线程
- 网络编程:Socket与JavaSE线程高级
- JavaSE 学习参考:Runnable接口实现线程任务
- JAVASE总结--线程的死锁
- 09 - JavaSE之线程
- JavaSE线程与进程的区别线程的创建与执行
- JavaSE 多线程 线程间通讯—等待唤醒机制代码优化(背下来)
- JavaSE线程笔记
- JAVASE学习(16)线程
- javaSE学习笔记之线程
- JavaSE学习 第十六章 线程
- JavaSE学习笔记--线程并发库
- JavaSE 基础 第53节 线程基本知识
- JavaSE学习 第十六章 线程
- JavaSE 学习参考:线程生命周期
- JavaSE 基础 第54节 继承Thread类创建线程
- JavaSE中线程与并行API框架学习笔记1——线程是什么?
- javaSE 线程(一)