【JavaSE笔记】多线程(一)进程&线程&Thread&同步代码块
2017-08-20 15:44
471 查看
本期知识点:
进程
线程
Thread
同步代码块
进程是系统进行资源分配和调用的独立单位,每一个进程都有自己的内存空间和系统资源
例如:我们一边玩游戏一边听音乐是同时进行的吗?
不是,单核CPU在某一个时间上只能做一件事情,而在玩游戏或者听歌的时候,CPU在做程序间高效的切换让人觉得是同时进行。
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位
单线程:程序只有一条执行路径
多线程:程序有多条执行路径
程序的执行其实是在抢CPU的资源、CPU的执行权。 多个程序是在抢资源,如果一个进程的执行路径比较多,就有更高的几率抢到CPU的执行权。
线程执行具有随机性。
并发:物理上同时发生,指在某一个时间点同时运行多个程序。
因为,垃圾回收线程也要先启动,否则很容易出现内存溢出。垃圾回收线程+主线程,至少启动了两个线程,所以是多线程。
java语言可以通过C/C++底层已经调用了系统功能
java提供了一个类:Thread 是程序中的执行线程。Java虚拟机允许应用程序并发地运行多个执行线程。
i.步骤:
1)自定义类MyThread继承Thread类
2)MyThread类里重写run()
3)创建对象
4)启动线程
(问题)为什么要重写run()?
因为不是类中所有代码都要被线程执行。为了区分哪些代码能够被线程执行,java提供了Thread类中的run()方法用来包含那些要被线程执行的代码
(问题)run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法。
start():首先启动了线程,然后再由JVM去调用该线程的run()方法。
public final String getName()返回该线程的名称。
如何设置线程的名称?
public final void setName(String name)改变线程名称,使之与参数 name 相同
public static Thread currentThread()返回对当前正在执行的线程对象的引用。
注意:线程默认优先级是5
注意:
1)会有异常java.lang.IllegalArgumentException
抛出的异常表明向方法传递了一个不合法或不正确的参数。
2)java.lang.Thread
public static final int MAX_PRIORITY 线程可以具有的最高优先级。 10
public static final int MIN_PRIORITY 线程可以具有的最低优先级。1
public static final int NORM_PRIORITY 分配给线程的默认优先级。5
优先级范围:1~10
3)线程优先级仅仅表示线程获取的CPU时间片的几率。要在多次运行时才能看到效果。
注意:父的方法没有异常抛出,子的重写方法不能有异常抛出只能 try...catch(这种情况只能子类中进行捕获异常);
让多个线程的执行更和谐但是不能保证一个一次
public void interrupt() 中断线程。把线程的状态终止,并抛出SecurityException
ii.就绪:有执行资格,没有执行权
iii.运行:有执行资格,有执行权
iv.阻塞:由于一些操作让线程处于该状态。没有执行资格,没有执行权。而另一些操作可以激活它,让他处于就绪状态。
v.死亡:线程对象变成垃圾,等待回收
i.步骤:
1)自定义一个类MyRunnable(同一个资源)实现Runnable接口
2)然后实现Rannable里面的run方法:耗时的操作
3)在主线程中创建MyRuhnable类的对象
4)创建Thread类对象,将第三步的对象作为参数进行传递来启动线程
ii.有了方式一为什么要出现方式二?(实现接口方式的好处)
例如:有个类已经继承了一个父类,而且父类不想继承Thread类
解决了单继承的局限性。
适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
ii.是否有共享数据
iii.是否有多条语句操作共享数据
思想:把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
需要被同步的代码;
}
注意:同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
多个线程使用的是同一个锁对象
缺点:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
这里的锁对象是this
这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)
i.
ii.
iii.
进程
线程
Thread
同步代码块
1.进程:
a.线程依赖于进程而存在
b.进程:
就是正在运行的程序进程是系统进行资源分配和调用的独立单位,每一个进程都有自己的内存空间和系统资源
c.多进程的意义:
在一段时间内执行多个任务,并且提高CPU的使用率例如:我们一边玩游戏一边听音乐是同时进行的吗?
不是,单核CPU在某一个时间上只能做一件事情,而在玩游戏或者听歌的时候,CPU在做程序间高效的切换让人觉得是同时进行。
2.线程:
a.概述:
在同一个进程内又可以执行多个任务,而每一个任务,可以i看作是一个线程线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位
单线程:程序只有一条执行路径
多线程:程序有多条执行路径
b.多线程的意义:
多线程的存在,不是为了提高程序的执行速度,其实是为了提高应用程序的使用率。程序的执行其实是在抢CPU的资源、CPU的执行权。 多个程序是在抢资源,如果一个进程的执行路径比较多,就有更高的几率抢到CPU的执行权。
线程执行具有随机性。
c.并行和并发的区别:
并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。并发:物理上同时发生,指在某一个时间点同时运行多个程序。
d.JVM程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动了一个进程,接着由该进程创建了一个主线程去调用main方法。(问题)JVM虚拟机的启动是多线程还是单线程?
多线程。因为,垃圾回收线程也要先启动,否则很容易出现内存溢出。垃圾回收线程+主线程,至少启动了两个线程,所以是多线程。
3.Thread
a.如何实现多线程程序?
java语言是不能直接调用系统功能(用系统功能创建对象)java语言可以通过C/C++底层已经调用了系统功能
java提供了一个类:Thread 是程序中的执行线程。Java虚拟机允许应用程序并发地运行多个执行线程。
b.多线程程序实现方法一
继承Thread类i.步骤:
1)自定义类MyThread继承Thread类
2)MyThread类里重写run()
3)创建对象
4)启动线程
(问题)为什么要重写run()?
因为不是类中所有代码都要被线程执行。为了区分哪些代码能够被线程执行,java提供了Thread类中的run()方法用来包含那些要被线程执行的代码
(问题)run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法。
start():首先启动了线程,然后再由JVM去调用该线程的run()方法。
public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100 ; i++) { System.out.println(i); } } } public class MyThreadDemo { public static void main(String[] args) { //创建线程对象 MyThread my = new MyThread(); //启动线程 my.start(); // my.start();//IllegalThreadStateException // 指示线程没有处于请求操作所要求的适当状态时抛出的异常。 // 相当于my线程被调用了两次,而不是两个线程启动 MyThread my2 = new MyThread(); my2.start(); } }
c.常用方法:
如何获取线程的名称?public final String getName()返回该线程的名称。
如何设置线程的名称?
public final void setName(String name)改变线程名称,使之与参数 name 相同
public static Thread currentThread()返回对当前正在执行的线程对象的引用。
public class MyThread extends Thread { public MyThread() { // TODO Auto-generated constructor stub } public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100 ; i++) { System.out.println(getName()+":"+i); } } } /* 设置名字的源码: public final synchronized void setName(String name) { checkAccess(); if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; if (threadStatus != 0) { setNativeName(name); } } */ public class MyThreadDemo { public static void main(String[] args) { // 无参构造+setXxx(); // //创建线程对象 // MyThread my1 = new MyThread(); // MyThread my2 = new MyThread(); // //设置名称 // my1.setName("Yang"); // my2.setName("Fan"); // 有参构造 MyThread my1 = new MyThread("Ash"); MyThread my2 = new MyThread("Glaz"); // 启动线程 my1.start(); my2.start(); // 获取main方法所在线程对象的名称? // public static Thread currentThread()返回对当前正在执行的线程对象的引用。 System.out.println(Thread.currentThread().getName()); } } /*为什么名称是:Thread-1:60 带有编号? Thread的无参构造 源码: private volatile String name; public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) { //省略代码 this.name = name; //省略代码 } private static int threadInitNumber;//0,1 private static synchronized int nextThreadNum() { return threadInitNumber++;//return 0,1 } public final String getName() { return name; } */
d.线程调度的两种模型
i.分时调度模型:
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片ii.抢占式调度模型(Java采用的是该调度方式)
优先让优先级高的线程使用CPU,如果线程优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。iii.如何获取默认优先级?
public final int getPriority()返回线程的优先级。注意:线程默认优先级是5
iv.如何设置优先级?
public final void setPriority(int newPriority)更改线程的优先级。注意:
1)会有异常java.lang.IllegalArgumentException
抛出的异常表明向方法传递了一个不合法或不正确的参数。
2)java.lang.Thread
public static final int MAX_PRIORITY 线程可以具有的最高优先级。 10
public static final int MIN_PRIORITY 线程可以具有的最低优先级。1
public static final int NORM_PRIORITY 分配给线程的默认优先级。5
优先级范围:1~10
3)线程优先级仅仅表示线程获取的CPU时间片的几率。要在多次运行时才能看到效果。
public class ThreadPriority extends Thread { @Override public void run() { for (int i = 0; i < 100 ; i++) { System.out.println(getName()+":"+i); } } } public class ThreadPriorityDemo { public static void main(String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority(); tp1.setName("Ash"); tp2.setName("Glaz"); tp3.setName("Ying"); //获取默认优先级 // System.out.println(tp1.getPriority());//5 // System.out.println(tp2.getPriority());//5 // System.out.println(tp3.getPriority());//5 //设置优先级 // tp1.setPriority(1000); // IllegalArgumentException // 抛出的异常表明向方法传递了一个不合法或不正确的参数。 //设置正确的优先级 tp1.setPriority(1); tp2.setPriority(10); tp1.start(); tp2.start(); tp3.start(); } }
e.线程的控制
i.休眠线程
public static void sleep(long millis) throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。注意:父的方法没有异常抛出,子的重写方法不能有异常抛出只能 try...catch(这种情况只能子类中进行捕获异常);
ii.加入线程
public final void join() throws InterruptedException 等待该线程终止。iii.礼让线程
public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。让多个线程的执行更和谐但是不能保证一个一次
iv.后台线程
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。v.中断线程
public final void stop()public void interrupt() 中断线程。把线程的状态终止,并抛出SecurityException
f.线程的生命周期
i.新建:创建线程对象ii.就绪:有执行资格,没有执行权
iii.运行:有执行资格,有执行权
iv.阻塞:由于一些操作让线程处于该状态。没有执行资格,没有执行权。而另一些操作可以激活它,让他处于就绪状态。
v.死亡:线程对象变成垃圾,等待回收
g.多线程程序实现方法二
实现 Runnable接口i.步骤:
1)自定义一个类MyRunnable(同一个资源)实现Runnable接口
2)然后实现Rannable里面的run方法:耗时的操作
3)在主线程中创建MyRuhnable类的对象
4)创建Thread类对象,将第三步的对象作为参数进行传递来启动线程
ii.有了方式一为什么要出现方式二?(实现接口方式的好处)
例如:有个类已经继承了一个父类,而且父类不想继承Thread类
解决了单继承的局限性。
适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100 ; i++) { //由于实现接口的方式不能直接使用Thread类的方法,但是可以间接使用 System.out.println(Thread.currentThread().getName()+":"+i); } } } public class MyRunnableDemo { public static void main(String[] args) { //创建MyRunnable类对象 MyRunnable mr = new MyRunnable(); //创建Thread类对象,把MyRunnable类对象作为构造参数传递 // public Thread(Runnable target) // Thread t1 = new Thread(mr); // Thread t2 = new Thread(mr); // // t1.setName("Ash"); // t2.setName("Ying"); // public Thread(Runnable target,String name) Thread t1 = new Thread(mr, "Ash"); Thread t2 = new Thread(mr, "Ying"); t1.start(); t2.start(); } }
4.解决线程安全问题的基本思想
a.卖票问题的问题
i.相同的票出现多次
CPU的一次操作必须是原子性的ii.出现了负数的票
随机性和延迟导致的//是否是多线程环境 是 //是否有共享数据 是 //是否有多条语句操作共享数据 是 //满足出问题的条件 public class SellTicket implements Runnable { //定义100张票 private int ticket = 100; @Override public void run() { while(true){ if(ticket>0){ try { Thread.sleep(100);//t1进来不休息,t2进来休息,t3进来休息 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } } } //实现Runnable接口的方法 public class SellTicketDemo { 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(); } }
b.首先想为什么出现问题?(也是我们判断是否有问题的标准)
i.是否是多线程环境ii.是否有共享数据
iii.是否有多条语句操作共享数据
c.如何解决?
i和ii的问题改变不了,只能想办法把C改变。思想:把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
5.同步代码块:
a.格式:
synchronized (对象){需要被同步的代码;
}
注意:同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
public class SellTicket implements Runnable { //定义100张票 private int ticket = 100; //创建锁对象 private Object obj = new Object(); @Override public void run() { while(true){ //t1,t2,t3都可以走到这里 //t1抢到执行权t1进 //门(开/关) synchronized (obj) {//t1进来后门关 if(ticket>0){ try { Thread.sleep(100);//t1睡眠 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } } } }
b.同步的前提
多个线程多个线程使用的是同一个锁对象
c.同步的优点和缺点:
优点:同步的出现解决了多线程的安全问题。缺点:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
d.同步解决线程安全问题
i:同步代码块
这里的锁对象可以是任意对象。ii:同步方法
把同步加在方法上。这里的锁对象是this
iii:静态同步方法
把同步加在方法上。这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)
i.
class Demo{ } public class SellTicket implements Runnable { //定义100张票 private int ticket = 100; //创建锁对象 private Object obj = new Object(); private Demo d = new Demo(); private int x = 0; // 同步代码块用Obj做锁 // @Override // public void run() { // while(true){ // synchronized (obj) { // if(ticket>0){ // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); // } // } // } // } // 同步代码块 // 这里的锁对象可以是任意对象。 // @Override // public void run() { // while(true){ // synchronized (d) { // if(ticket>0){ // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); // } // } // } // } //} @Override public void run() { while(true){ if(x%2==0){ synchronized (d) { if(ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } }else{ // synchronized (d) { // if(ticket>0){ // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); // } // } sellTicket(); } } } //如果一个代码一进去就同步 把同步加在方法上 private void sellTicket() { // synchronized (d) { // if(ticket>0){ // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); // } // } } }
ii.
class Demo{ } public class SellTicket implements Runnable { //定义100张票 private int ticket = 100; //创建锁对象 private Object obj = new Object(); private Demo d = new Demo(); private int x = 0; @Override public void run() { while(true){ if(x%2==0){ synchronized (this) { if(ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } }else{ sellTicket(); } } } //如果一个代码一进去就同步 把同步加在方法上 private synchronized void sellTicket() { if(ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } }
iii.
class Demo{ } public class SellTicket implements Runnable { //定义100张票 private static int ticket = 100; //创建锁对象 private Object obj = new Object(); private Demo d = new Demo(); private int x = 0; @Override public void run() { while(true){ if(x%2==0){ synchronized (SellTicket.class) { if(ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } }else{ sellTicket(); } } } private static synchronized void sellTicket() { if(ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票"); } } }
相关文章推荐
- java-学习笔记-线程&进程
- Java多线程高并发学习笔记(一)——Thread&Runnable
- 多线程 学习笔记<4> 进程与线程的区别联系
- <Boost> boost::thread 多线程的使用
- python学习笔记——多进程中共享内存Value & Array
- 进程&线程(&java.lang.Thread)详解
- Python笔记-进程Process、线程Thread、上锁
- 【我的Java笔记】多线程安全问题 & 同步机制
- Android(java)学习笔记215:多线程断点下载的原理(JavaSE实现)
- C# 使用Thread多线程,窗体关闭后进程不退出的解决方案
- Operating System-Thread(5)弹出式线程&&使单线程代码多线程化会产生那些问题
- .net多线程学习笔记 2 thread生命周期
- 多进程 & 多线程的区别与适用场景
- RT-Thread 学习笔记(十二)--- 开启基于RTGUI的LCD显示功能(2)<编译测试>
- IOS学习笔记23—多线程之NSThread、NSOperation、NSInvocationOperation
- "Scalable Multithreaded Programming with Thread Pools" 阅读笔记
- IOS学习笔记23—多线程之NSThread、NSOperation、NSInvocationOperation
- python进阶学习笔记(四)--多线程thread
- 菜鸟备忘录[通识]——线程&进程;并行&并发;同步&异步and阻塞&非阻塞 必看必晕
- 多进程和多线程的练习笔记