从零开始的"E"世界(J2SE)
来源:互联网 发布:最后的堡垒 知乎 编辑:程序博客网 时间:2024/05/06 09:56
JavaNote8
- JavaNote8
- 多线程
- 1 并发和并行
- 2 进程和线程
- 3 多线程优势
- 4 创建和启动线程
- 5 线程同步
- 5 线程通信
- 6 线程的六大状态
- 6 经典案例 生产者和消费者
- JavaNote8
1. 多线程
1.1 并发和并行
并行:指两个或多个事件在同一时刻点发生;
并发:指两个或多个事件在同一时间段内发生。
1.2 进程和线程
进程:进程是指一个内存中运行中的应用程序。每个进程都有自己独立的一块内存空间.
线程:线程是指进程中的一个执行任务(控制单元),一个进程可以同时并发运行多个线程.
多进程:操作系统中同时运行的多个程序。
多线程:在同一个进程中同时运行的多个任务。
进程与线程的区别:
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的,又称为轻型进程或进程元。
注意:Java程序的进程(Java的一个程序运行在系统中)里至少包含主线程和垃圾回收线程(后台线程)。
1.3 多线程优势
- 进程之前不能共享内存,而线程之间共享内存(堆内存)则很简单。
- 系统创建进程时需要为该进程重新分配系统资源,创建线程则代价小很多,因此实现多任务并发时,多线程效率更高.
- Java语言本身内置多线程功能的支持,而不是单纯第作为底层系统的调度方式,从而简化了多线程编程.
1. 4 创建和启动线程
创建和启动线程,有两种方式:
方式1:继承Thread类;
方式2:实现Runnable接口;
线程类(java.lang.Thread): Thread类和Thread的子类才能称之为线程类.
方式1:继承Thread类:
步骤:
1:定义一个类A**继承于java.lang.Thread类**.
2:在A类中覆盖Thread类中的run方法.
3:我们在run方法中编写需要执行的操作—->run方法里的,线程执行体.
4:在main方法(线程)中,创建线程对象,并启动线程.
创建线程类对象: A类 a = new A类();
调用线程对象的start方法: a.start();//启动一个线程
//播放音乐的线程类class MusicThread extends Thread{ public void run() { for(int i = 0; i < 50; i++){ System.out.println("播放音乐" + i); } }}//方式1:继承Thread类public class ExtendsThreadDemo { public static void main(String[] args) { //主线程:运行游戏 for(int i = 0; i < 50; i++){ System.out.println("打游戏" + i); if(i == 10){ //创建线程对象,并启动线程 MusicThread t = new MusicThread(); t.start();//不能调用run方法 } } }}
注意:千万不要调用run方法,如果调用run方法好比是对象调用方法,依然还是只有一个线程,并没有开启新的线程.
方式2:实现Runnable接口;
步骤:
1):定义一个类A实现于java.lang.Runnable接口,注意A类不是线程类.
2:在A类中覆盖Runnable接口中的run方法.
3:我们在run方法中编写需要执行的操作—->run方法里的,线程执行体.
4:在main方法(线程)中,创建线程对象,并启动线程.
创建线程类对象: Thread t = new Thread(new A());
调用线程对象的start方法: t.start();
//播放音乐的实现类class MusicRunnableImple implements java.lang.Runnable{ public void run() { for(int i = 0; i < 50; i++){ System.out.println("播放音乐" + i); } }}//方式2:实现Runnable接口public class ImplementsRunnableDemo { public static void main(String[] args) { //主线程:运行游戏 for(int i = 0; i < 50; i++){ System.out.println("打游戏" + i); if(i == 10){ //创建线程对象,并启动线程 Thread t = new Thread(new MusicRunnableImple()); t.start(); } } }}
1. 5 线程同步
当多线程并发访问同一个资源对象的时候,可能出现线程不安全的问题.
解决方案:
方式1:同步代码块
方式2:同步方法
方式3:锁机制(Lock)
同步代码块
//同步代码块语法:synchronized(同步锁){ //需要同步操作的代码}
同步锁:
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制.
同步监听对象/同步锁/同步监听器/互斥锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
Java程序运行使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象.
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着.
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着.
同步方法synchronized public void doWork(){ ///TODO}
同步锁是谁:
对于非static方法,同步锁就是this.
对于static方法,我们使用当前方法所在类的字节码对象.
synchronized的好与坏:
好处:保证了多线程并发访问时的同步操作,避免线程的安全性问题.
缺点:使用synchronized的方法/代码块的性能比不用要低一些.
建议:尽量减小synchronized的作用域.
同步锁(Lock):
Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象.
class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { //m方法是需要同步操作的方法 lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } }
1. 5 线程通信
1.线程通信-wait和notify方法
注意:线程通信:不是传输数据,而是控制线程状态.
java.lang.Object类提供类两类用于操作线程通信的方法.
wait():执行该方法的线程对象释放同步锁,JVM把该线程存放到等待池中,等待其他的线程唤醒该线程.
notify():执行该方法的线程唤醒在等待池中等待的任意一个线程,把线程转到锁池中等待.
notifyAll():执行该方法的线程唤醒在等待池中等待的所有的线程,把线程转到锁池中等待.
注意:上述方法只能被同步监听锁对象来调用,否则报错IllegalMonitorStateException
线程通信-使用Lock和Condition接口:
从Java5开始,可以:
1):使用Lock机制取代synchronized 代码块和synchronized 方法.
2):使用Condition接口对象的await,signal,signalAll方法取代Object类中的wait,notify,notifyAll方法.
1. 6 线程的六大状态
- 1:新建状态(new):使用new创建一个线程对象,仅仅在堆中分配内存空间,在调用start方法之前.新建状态下,线程压根就没有启动,仅仅只是存在一个线程对象而已.
Thread t = new Thread();//此时t就属于新建状态
当新建状态下的线程对象调用了start方法,此时从新建状态进入可运行状态.线程对象的start方法只能调用一次,否则报错:IllegalThreadStateException.
2:可运行状态(runnable):分成两种状态,ready和running。分别表示就绪状态和运行状态。
- 就绪状态:线程对象调用start方法之后,等待JVM的调度(此时该线程并没有运行).
- 运行状态:线程对象获得JVM调度,如果存在多个CPU,那么允许多个线程并行运行.
3:阻塞状态(blocked):正在运行的线程因为某些原因放弃CPU,暂时停止运行,就会进入阻塞状态.
此时JVM不会给线程分配CPU,直到线程重新进入就绪状态,才有机会转到运行状态.
阻塞状态只能先进入就绪状态,不能直接进入运行状态.
阻塞状态的两种情况:- 1:当A线程处于运行过程时,试图获取同步锁时,却被B线程获取.此时JVM把当前A线程存到对象的锁池中,A线程进入阻塞状态.
- 2):当线程处于运行过程时,发出了IO请求时,此时进入阻塞状态.
4:等待状态(waiting)(等待状态只能被其他线程唤醒):此时使用的无参数的wait方法,
- 当线程处于运行过程时,调用了wait()方法,此时JVM把当前线程存在对象等待池中.
5:计时等待状态(timed waiting)(使用了带参数的wait方法或者sleep方法)
- 1:当线程处于运行过程时,调用了wait(long time)方法,此时JVM把当前线程存在对象等待池中.
- 2:当前线程执行了sleep(long time)方法.
6:终止状态(terminated):通常称为死亡状态,表示线程终止.
- 1:正常执行完run方法而退出(正常死亡).
- 2:遇到异常而退出(出现异常之后,程序就会中断)(意外死亡).
注意:线程一旦终止,就不能再重启启动,否则报错(IllegalThreadStateException).
1. 6 经典案例 生产者和消费者
//消费者public class Consumer extends Thread { private Product p; public Consumer(Product p) { this.p = p; } public void run() { for (int i = 0; i < 50; i++) { p.consume(); } }}//生产者public class Producer extends Thread { private Product p = null; public Producer(Product p) { this.p = p; } public void run() { for (int i = 0; i < 50; i++) { if (i % 2 == 0) { p.produce("红旗", "中国"); } else { p.produce("三菱", "日本"); } } }}//产品public class Product { private String productName;//产品名称 private String country;//生产国家 private Lock lock = new ReentrantLock(); private Condition con_pro = lock.newCondition();//生产的监视器 private Condition con_con = lock.newCondition();//消费的监视器 private boolean isEmpty = true;//判断产品是否为空 //生产 public void produce(String productName, String country) { lock.lock(); try { while (!isEmpty) { //如果为false(产品不为空),生产的监视器等待 con_pro.await(); } this.productName = productName; Thread.sleep(30); this.country = country; isEmpty = false; con_con.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } //消费 public void consume() { lock.lock(); try { while (isEmpty) { //如果为true(产品为空),消费的监视器等待 con_con.await(); } Thread.sleep(30); System.out.println(this.productName + "--->" + this.country); isEmpty = true; con_pro.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }}
最后附上Java学习网址
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的"E"世界(J2SE)
- 从零开始的异世界生活
- 从零开始进入ArchLinux的世界-安装
- 【开始】从零开始,进军崭新的世界
- sdut oj3915从零开始的异世界生活
- PAT乙级1022. D进制的A + B(20)
- PCL点云库网上资料收集汇总(临时,长期维护)
- Spring cloud系列五 Eureka 之集群同步、自我保护模式以及实例注册、心跳、下线
- linux设备驱动归纳总结
- Linux IPC 3 之信号
- 从零开始的"E"世界(J2SE)
- 实验三 链队列
- Linux exit() 和 return 的区别
- linux nobody用户是什么
- php钩子的应用
- Android更换头像上传
- makedownpad使用
- The Matrix--庄生晓梦迷蝴蝶
- 用模板类实现顺序表