< 笔记 > Java SE - 04 Java SE 多线程
2017-10-05 17:33
489 查看
04 Java SE 多线程
By Kevin Song04-01 多线程概述
04-02 JVM中的多线程
04-03 多线程创建
04-04 多线程同步
04-05 多线程中的单例模式
04-06 多线程死锁
04-07 多线程通信
04-08 多线程停止
04-01 多线程概述
进程:正在进行中的程序线程:进程中的一个负责程序执行的控制单元(执行路径)
一个进程中可以有多个线程
一个进程中至少有一个线程
多线程作用:开启多个线程是为了同时运行多部分代码
多线程的优缺点:
优点:可以同时运行多个程序
缺点:内存处理到程序频率变低,运行速度变慢
04-02 JVM中的多线程
JVM启动时就启动了多个线程,至少有两个线程执行主方法的线程:该线程的任务代码都定义在主方法中
负责垃圾回收的线程
Object 类中的 finalize() 方法:
当垃圾回收器确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法
System类中的 System.gc() 方法:运行垃圾回收器,垃圾回收器运行时间随机
class Demo extends Object { public void finalize() { System.out.println("Recycle Complete"); } } class ThreadDemo { public static void main(String[] args) { new Demo(); new Demo(); System.gc(); // new Demo(); System.out.println("Hello World!"); } } /* 输出 Hello World! Recycle Complete Recycle Complete 因为是两个线程,先运行主线程,JVM关闭前运行垃圾回收线程 */
04-
4000
03 多线程创建
主方法单线程运行不创建多线程
class Demo { private String name; Demo (Srting name) { this.name = name; } public void show() { for(int x = 0; x < 10; x++) { //y的for循环让每次输出都有一定的延迟,但是必须d1都输出完才输出d2,这时就需要多线程来让他们同时输出 for(int y =-9999999; y < 999999999; y++) {} System.out.println(name+"...x="+x); } } } class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("Sequence Initializing"); Demo d2 = new Demo("序列初始化中"); d1.show(); d2.show(); } }
创建多线程
目的:开启一条执行路径,使得指定的代码和其他代码实现同时运行,而此执行路径运行的指定代码就是这个执行路径的任务任务存放位置:
- JVM创建的主线程的任务都定义在了主方法中
- 自定义线程的任务定义在Thread类的run方法中
创建多线程有两种方法
继承Thread类
实现Runnable接口
创建线程方式一
继承Thread类
步骤:
定义一个类,继承Thread类
重写Thread类中的 run(); 方法
直接创建Thread类的子类对象(创建线程)
调用 start(); 方法开启线程,并调用线程的任务run(); 方法
class Demo extends Thread{ private String name; Demo (Srting name) { this.name = name; } public void run() { for(int x = 0; x < 10; x++) { //currentThread()获取当前运行中线程的引用 System.out.println(name+"...x="+x+"...Thread Name="+Thread.currentThread().getname()); } } } class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("Sequence Initializing"); Demo d2 = new Demo("序列初始化中"); d1.start();//开启线程,调用run方法 d2.start();//开启线程,调用run方法 //CPU在主线程,d1,d2之间随机高速切换 } }
Thread类中的方法&线程名称
getName()方法
获取线程的名称Thread-编号(从0开始)
主线程的名字main
currentThread()方法
获取当前运行中线程的引用,该方法为静态,可以直接被类名调用
多线程异常问题
有异常的线程结束运行,run() 方法出栈,线程之间互不影响。
线程的状态
new被创建
start() 运行(具备执行资格,具备执行权)
冻结(释放执行权,释放执行资格)
sleep(time) 冻结 时间到自动唤醒
wait() 冻结 notify 唤醒
阻塞(具备执行资格,不具备执行权,正在等待执行权)
消亡
run() 方法结束
stop() 停止线程
CPU的执行资格:可以被CPU处理,在处理队列中排队
CPU的执行权:正在被CPU处理
创建线程方式二
实现Runnable接口
步骤:
定义一个类,实现Runnable接口
重写接口中的run方法,将线程的任务代码封装到run方法中
通过Thread类创建线程对象,将Runnable接口的子类对象作为构造方法的参数进行传递
调用线程对象的start(); 方法开启线程
class Demo extends Fu implements Runable{ //无法继承Thread但是需要多线程,通过接口形式完成 public void run() { show(); } public void show() { for(int x = 0; x < 20; x++) { System.out.println(Thread.currentThread().getName()+"..."+x); } } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start; t2.start; } }
第二种方法的好处:
将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象
避免了java单继承的局限性
04-04 多线程同步
卖票示例四个线程,每个线程都卖100张票
class Ticket extends Thread{ private int num = 100; public void run() { sale(); } public void sale() { while(true) { if(num > 0) { System.out.println(Thread.currentThread().getName()+“...sale...”num--); } } } } class TicketDemo { public static void main(String[] args) { //四个对象,每个对象都有100张票 Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); } }
四个线程一起卖100张票
class Ticket implements Runnable { private int num = 100; public void run() { sale(); } public void sale() { while(true) { if(num > 0) { System.out.println(Thread.currentThread().getName()+“...sale...”num--); } } } } class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); //一个对象,所有一共只有100张票 Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); } }
线程安全问题的现象
会出现0,-1,-2张票的情况,因为线程1还没运行到num–的时候,线程2,线程3有可能就已经加载进来,当num–运行完之后,线程还会继续运行,这就导致0,-1,-2的出现。
安全问题产生的原因
多个线程在操作共享的数据
操作共享数据的线程代码有多条
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算
多线程同步
将多条操作共享的数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。同步代码块
格式
synchronized(对象) { 需要被同步的代码; }
class Ticket implements Runnable { private int num = 100; Object obj = new Object; //此对象为了传给synchronized作为锁 public void run() { sale(); } public void sale() { while(true) { synchronized(obj) { //synchronized修饰的代码块只能同一时间被一个线程调用 if(num > 0) { try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println(Thread.currentThread().getName()+“...sale...”num--); } } } } } class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); //一个对象,所有一共只有100张票 Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); } }
同步的前提:
同步中必须有多个线程并使用同一个锁
当作锁的对象必须在 run() 方法外创建
同步的优缺点:
优点:解决了线程的安全问题
缺点:相对降低了效率,因为同步外的线程都会判断同步锁
同步方法
/* 两个客户每次去银行存100,存三次 */ class Bank { private int sum; private Object obj = new Object(); public synchronized void add(int num) {//同步方法 // synchronized(obj) { sum = sum + num; System.out.println("sum="+sum); // } } } cl 1172e ass Cus implements Runnable { private Bank b = new Bank(); public void run() { for(int x = 0; x < 3; x++) { b.add(100); } } } class BankDemo { public static void main(String[] args) { Cus c = new Cus; Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } } /* 输出: sum=100 sum=200 sum=300 sum=400 sum=500 sum=600 */
同步代码块和同步方法同时使用
class Ticket implements Runnable { private int num = 100; Object obj = new Object; //此对象为了传给synchronized boolean flag = true; public void run() { if(flag)//flag为true则运行同步代码快 while(true) { synchronized(this) { if(num>0) { try { Thread.sleep(10); } catch(InterruptedException e) {} System.out.println(Thread.currentThread().getName()+"...obj..."+num--); } } } else //flag为false则运行同步方法 while(true) this.show(); } public synchronized void show() { if(num > 0) { try { Thread.sleep(10); } catch () System.out.println(Thread.currentThread().getName()+“...syn...”num--); } } } class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); //一个对象,所有一共只有100张票 Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); t1.start(); try { Thread.sleep(10); } catch(InterruptedException e) {} t.flag = false; t2.start(); } }
同步方法和同步代码快的区别:
同步方法的锁是固定的this(当前对象)
同步代码块的锁是任意的对象
静态同步方法使用的锁:
该方法所属字节码文件对象,可以用如下方式获取
this.getClass() 仅限非静态方法中使用
Ticket.class
04-05 多线程中的单例模式
使用同步虽然可以避免安全问题,但是会导致程序运行效率变低,因为每个线程都需要判断是否可以拿锁//饿汉式(单例设计模式) class Single { private static final Single s = new Single(); private Single() {} public static Single getInstance() { return s; } } //懒汉式(延迟加载单例设计模式) class Single { private static Single s = null; private Single() {} public static Single getInstance() { if(s == null) {//解决效率问题:多加一句判断,后续线程不会判断是否可以拿锁 //不用this.getClass()是因为getClass()是非静态方法 synchronized(Single.class) {//解决安全问题 if(s == null) s = new Single(); } } return s; } } class SingleDemo { public static void main(String[] args) { System.out.println(); } }
04-06 多线程死锁
同步代码块嵌套class Test implements Runnable{ private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if(flag) { synchronized(MyLock.locka) { System.out.println("if...locka..."); synchronized(MyLock.lockb) { System.out.println("if...lockb..."); } } } else { synchronized(MyLock.lockb) { System.out.println("if...lockb..."); synchronized (MyLock.locka) { System.out.println("if...locka..."); } } } } } class MyLock { public static final Object locka = new Object(); public static final Object lockb = new Object(); } class DeadLockTest { public static void main(String[] args) { Test a = new Test(true); Test b = new Test(false); Thread t1 = new Thread(a); Thread t2 = new Thread(b); t1.start; t2.start; } }
04-07 多线程通信
定义:多个线程在处理同一资源,任务却不同等待唤醒机制
涉及的方法:wait(); 让线程处于冻结状态,释放执行权和执行资格,存储到线程池中
notify(); 唤醒线程池中一个线程
notifyAll(); 唤醒线程池中所有线程,防止多生产多消费的死锁问题
这些方法都必须定义在同步中,因为这些方法都是用于线程状态的方法,必须要明确到底操作的是哪个锁上的线程。
等待唤醒机制中的方法(wait(); notify(); notifyAll();)定义在Object类中,因为这些方法是监视器的方法,监视器其实就是锁
//资源 class Resource { private String name; private String sex; privateboolean flag = false; //用来判断是否可以输入输出 public synchronized void set(String name) { if(flag) //如果flag为false,则不wait try { this.wait(); } catch (InterruptException e) {} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if(!flag) try { this.wait(); } catch (InterruptException e) {} System.out.println(name+"..."+sex); flag = false; notify(); } } //输入 class Input implements Runnable { Resource r; Input(Resource r) { this.r = r; } public void run() { int x = 0; while(true) { if(x == 0) { //控制两种数据输入 r.set("Kevin","Male") } else { r.set("Lily","Female"); } x = (x+1)%2; } } } //输出 class Output implements Runnable { Resource r; Output(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } } class ResourceDemo { public static void mian(String[] args) { //创建资源 Resource r = new Resource(); //创建任务 Input in = new Input(r); Output out = new Output(r); //创建线程 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //开启线程 t1.start(); t2.start(); } }
多生产者多消费者问题
多生产多消费与单生产单消费的区别
flag判断语句区别
if只判断标记一次,会导致不该运行的线程开始运行,出现数据错误
while可以循环判断标记,解决了线程获取执行权后,是否要运行的问题
唤醒语句区别
notify()只能唤醒一个线程,如果唤醒了本方,没有意义,并且会导致死锁,所有线程都进线程池
notifyAll()解决了,本方线程一定会唤醒对方线程
//定义资源 class Resource { private String name;//资源名称 private int count = 1;//计数器 private boolean flag = false; public synchronized void set(String name) { while(flag)//flag为false时不wait,直接运行下面内容。 try { this.wait();//当flag为true时进入wait状态 } catch (InterruptException e) {} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag = true; //flag改成true notify(); //随机唤醒一个线程,如果是多生产多消费者则用notifyAll(); } public synchronized void out() { while(!flag) try { this.wait(); } catch (InterruptException e) {} System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag = false; notify();//随机唤醒一个线程,如果是多生产多消费者则用notifyAll(); } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("烤鸭"); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(con); t0.start(); t1.start(); } }
JDK1.5新特性
LockLock接口:代替了同步代码快或者同步方法,将同步的隐式锁操作变成显式锁操作,更为灵活,可以一个锁上加多组监视器。
lock(); 获取锁
unlock(); 释放锁,通常需要定义在finally代码块中
Lock lock = new ReentrantLock(); public void run() { lock.lock(); try { code; } finally { lock.unlock(); } }
Condition
Condition接口:代替了Object中的wait notify notifyAll方法。这些监视器被封装,变成Condition监视器对象,可以任意锁进行组合
await(); 功能同wait()
signal(); 同notify()
signalAll(); 同notifyAll()
1.5版本的多生产者多消费者代码
//定义资源 class Resource { private String name;//资源名称 private int count = 1;//计数器 private boolean flag = false; //创建一个锁对象 Lock lock = new ReentrantLock(); //通过已有的锁获取该锁上的监视器对象 //Condition con = lock.newCondition(); Condition Producer_con = lock.newCondition(); Condition Consumer_con = lock.newCondition(); public void set(String name) { lock.lock(); try { while(flag)//flag为false时不wait,直接运行下面内容。 try { Producer_con.await();//当flag为true时进入wait状态 } catch (InterruptException e) {} this.name = name + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name); flag = true; //flag改成true //notifyAll(); //con.signalAll(); consumer_con.signal(); } finally { lock.unlock(); } } public synchronized void out() { lock.lock(); try { while(!flag) try { consumer_con.await(); } catch (InterruptException e) {} System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag = false; //notifyAll(); //con.signalAll(); //Producer_Con.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(con); t0.start(); t1.start(); } }
wait和sleep区别
时间指定不同
wait可以指定时间也可以不指定
sleep**必须**指定时间
在同步中时,对CPU的执行权和锁的处理不同
wait:释放执行权,释放锁
sleep:释放执行权,不释放锁
04-08 多线程停止
停止线程
stop方法,已经过时定义标记,使run方法结束
定义标记
定义一个标记flag,当flag为true时线程可以运行,当flag为负时线程停止运行。
局限性:当有wait() 的时候会导致线程无法继续判断flag而进程冻结
class StopThread implements Runnable { boolean flag = true; //定义flag标记 public void run() { while(flag) { //flag标记为true时运行run方法 System.out.println(Thread.currentThread().getName()+"..."); } } public void setFlag() { //定义设置flag为false的方法 flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int num = 1; for(;;) { if(++num==50) { st.setFlag(); break; } System.out.println("main..."+num); } System.out.println("over"); } }
interrupt()方法
Interrput() 方法可以把线程从冻结状态强制恢复到运行状态中来
class StopThread implements Runnable { boolean flag = true; //定义flag标记 public synchronized void run() { while(flag) { //flag标记为true时运行run方法 try { wait(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getNmae()+"..."+e); flag = false; } System.out.println(Thread.currentThread().getName()+"..."); } } public void setFlag() { //定义设置flag为false的方法 flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int num = 1; for(;;) { if(++num==50) { //st.setFlag(); t1.interrupt();//强制恢复运行 t2.interrupt();//强制恢复运行 break; } System.out.println("main..."+num); } System.out.println("over"); } }
setDaemon()方法
使线程变成守护线程(后台线程),前台线程结束之后后台线程自动结束
join()方法
执行权让给t1
t1.join();
setPriority()方法
设置线程优先级
t1.setPriority(Thread.MAX_PRIORITY);
MAX_PRIORITY
MIN_PRIORITY
NORM_PRIORITY
相关文章推荐
- < 笔记 > Java SE - 07 Java SE GUI
- < 笔记 > Java SE - 08 Java SE 网络编程
- < 笔记 > Java SE - 06 Java SE IO
- < 笔记 > Java SE - 09 Java SE 反射
- < 笔记 > Java SE - 01 Java SE 概述
- < 笔记 > Java SE - 10 Java SE 正则表达式
- <笔记>.NET基础知识04
- < 笔记 > JavaScript - 04 JavaScript 语句
- < 笔记 > Python - 04 Python 高级特性
- <MFC笔记>多线程编程之线程的特性
- <<Linux内核完全剖析 --基于0.12内核>>学习笔记 第4章 80x86保护模式及其编程 4.8 保护模式编程初始化
- <C和指针----读书笔记1>
- <MFC多线程> 一道迅雷多线程编程题
- <java编程思想>(thing in java) 阅读笔记(第十六章至第二十一章)
- Accelerated C++学习笔记1—<开始学习C++>
- <转>iOS 多线程 RunLoop 机制 (二)
- <高性能JavaScript>笔记 [7~10]
- <数据结构算法与应用(C++语言描述)>学习笔记
- <<SQL Server 2005 高级程序设计>> 学习笔记(3)
- <<深入Java虚拟机>>-第二章-Java内存区域-学习笔记