Java语言高编——多线程(并发)
来源:互联网 发布:tcl液晶电视网络连接 编辑:程序博客网 时间:2024/06/05 15:13
线程的并发和并行
并发:同一个CPU在同一时间模拟对不同的代码进行运行。
并行:不同的CPU在同一时间对不同的代码进行运行。
进程(一个Java程序是独立的进程)
进程:指一个程序,在内存占有一块空间(资源),一个进程至少会包含一个线程(主线程main)。
线程(一个执行单元)
线程:是进程执行的单位,多个线程共享进程资源。
多线程
多线程:是指这个程序(一个进程)运行时产生了不止一个线程。
线程的创建和启动
线程的创建启动的方法有两种:
1.继承Thread类,Java类是支持多线程的,通过java.lang.Thread类来实现,Thread中的相关函数可以启动线程,终止线程、线程挂起等;
创建:继承Thread类,并重写run方法。
启动:创建一个该类的对象,调用start方法。
实现代码:
创建
public class MyThread extends Thread{ public void run(){ }}
启动
MyThread thread = new MyThread();thread.start();
2.提供一个实现Runnable接口的类作为一个线程的目标对象,在初始化一个Thread类或Threed子类的线程对象时,把目标对象从Runnable得到run()方法。这个定义方法仍然可以继承其它的类。
创建:实现Runnable或者Callable接口
启动:创建Runnable或者Callable接口的实例,创建一个Thread对象,将Runnable对象传入Thread对象,调用start方法。
实现代码(包含两种):
方式A(常用):
创建
public class MyThread implements Runnable{ public void run(){ for(int i=0;i<100;i++;){ System.out.println(Thread.currentThread().getName()+i); try{ Thread.sleep(50) }catch(InterruptedException e){ e.printStackTarce(); } } } }
启动
Thread thread =new Thread(new MyRunnable());thread.start();
方式B(可以拿到线程的执行结果的返回值和抛异常):
创建
import java.util.concurrent.Callable;public class MyCallable implements Callable<String>{public String call() throws Exception{ for(int i=0;i<100;i++;){ System.out.println(Thread.currentThread().getName()+i); Thread.sleep(50); } return "fanhui"; }}
启动
MyCallable callable = new MyCallable();FutureTask<String> futureTask =new FutureTask<String>(callable);new Thread(futureTask,"childThread").start();String str =futureTask.get();
run()和start()方法的使用
把需要处理的代码放到run()方法中,start()方法启动线程将调用run()方法,这个由java的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。不要直接调用run()方法,否则并没有开启新的线程去执行。
线程生命周期
新建状态
使用new关键字和Thread类及其子类建立一个线程对象后,该对象就处于新建状态。它保持这个状态直到start()方法开启这个线程。
当一个线程处于新建状态时,它仅仅是一个空的线程对象,系统不为它分配资源。
就绪状态
当线程对象调用start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程线程调度器的调度。
thread.start();//调用start()方法
运行状态
如果就绪状态的线程获取CPU资源,就可以执行run(),此时线程便处于运行状态。此处运行状态的线程最为复杂,它可以变为阻塞状态,就绪状态和死亡状态。
阻塞状态
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行进入阻塞状态。在睡眠时间已到或者获得设备资源后可以进入就绪状态。
死亡状态
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程控制
线程睡眠
Thread.sleep(millis);//静态方法,使用类名调用。使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行。
线程加入
thread.join();//让当前线程等待thread线程结束之后才能继续运行thread.join(millis);//让当前线程等待thead线程millis毫秒后运行
线程让步
thread.yield();//该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只是让同优先级的线程有执行的机会。
后台程序
thread.setDaemon(boolean on);//设置后台线程
后台线程会随着主程序的结束而结束,但是前台进程则不会;或者说只要一个前台线程未退出,进程就不会终止。
默认情况下,程序员创建的线程的是用户线程:用setDaemon(ture)可以设置线程为后台程序;而用isDemon()则可以判断一个线程是前台线程还是后台线程;
注意:setDemon函数必须在start函数之前设定,否则会抛出IllgalThreadStateException异常。
线程优先级
在Java中,每一个线程都有一个优先级。默认情况,一个线程将继承其父线程的优先级。线程用数字来表示,范围从1到10,一个线程的缺省优先级是5
Thread.MIN_PRIORITY = 1Thread.MAX_PRIORITY = 10Thread.NORM_PRIORITY = 5
使用下述方法获得或设置线程对象的优先级
int getPriority();void setPriority(int newPriority);
可以通过setPriority方法(final的,不能被子类重载)更改优先级。优先级不能超出1-10的取值范围,否则抛出IllageArgumentException异常。
注意:不要假定高优先级线程一定先于低优先级的线程执行,不要有逻辑依赖于线程优先级,否则可能产生意外结果。
线程组
有些程序包含了相当数量的线程。这时,如果按照线程的功能将他们分成不同的类别将很有用。
线程组可以用来同时对一组线程进行操作。
创建线程组
hreadGroup g=new ThreadGroup(groupName);
将各个线程添加给该线程组:
Thread t=new Thread(g, threadName);
要中断某个线程组中所有线程的运行:
g. interrupt();
要确定某个线程组中的任一线程是否仍处于可运行状态:
if (g. activeCount()==0)
线程组有一个很好的特性,那就是如果某个线程由于异常事件而成为死线程,你将会得到通知
对象监视器(锁)
在java中,每个对象都包含了一把锁(也叫作“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。在给定时刻,只有一个线程可以拥有一个对象的监视器。
同步(synchronized)
为了确保在任何时刻一个共享对象只被一个线程使用,必须使用“同步(synchronized)”
有两种方式实现同步:
使用同步方法
synchronized void methodA() { }
使用同步代码块
synchronized(obj){//obj是被锁定的对象 //要同步的语句}
用synchronized来标识的块或方法即为监视器监视的部分。只有使用synchronized ,才能利用对象的监视器功能。
调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自己的工作,并解除锁定。因此,一个特定对象的所有synchronized方法都共享着一把锁,而且这把锁能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)。
每个类也有自己的一把锁(作为类的Class对象的一部分),所以synchronized static方法可在一个类的范围内被相互间锁定起来,防止与static数据的接触。一般情况下,只在方法的层次上使用同步
生产者-消费者问题
在多线程程序中,可能出现生产者-消费者问题,即等待同步数据的问题。
可能出现的问题是:
生产者比消费者快时,消费者会漏掉一些数据没有取到
消费者比生产者快时,消费者取相同的数据
notify()和wait ()方法用来协调读取的关系
notify()和wait ()都只能从同步方法中调用
wait-notify 机制
- 当synchronized方法中的wait方法被调用时,当前线程将被中断运行,并且放弃该对象的锁。
- 一旦线程调用了wait方法,它便进入该对象的等待列表。要从等待列表中删除该线程,使它有机会继续运行,其它线程必须调用同一个对象上的notify或者notifyAll方法。
- 当线程再次成为可运行的线程后,它们便试图重新进入该对象。一旦可以使用该对象锁时,其中的一个线程将锁定该对象,并且从它上次调用wait方法后的位置开始继续运行。
使用同步机制
1.如果两个或多个线程修改一个对象,请将执行修改的方法声明为synchronized方法。受到对象修改影响的只读方法也必须实现同步。
2.如果一个线程必须等待某个对象的状态出现变更,那么它应该在对象的内部等待而不是在外边等待。这可以通过进入一个synchronized方法,并且调用wait方法来实现。
3.不要在synchronized方法中花费大量的时间。大多数操作只是更新数据,然后很快返回。
4.每当一个方法改变某个对象的状态时,它就应该调用notifyAll方法。这样可以给等待线程一个机会,以便查看环境有没有发生变化
5.记住,wait和notifyAll/notify方法都属于Object类的方法,而不是Thread类的方法。反复检查你对wait方法的调用与同一对象上的通知是否匹配。
死锁
定义:当所有的线程都在等待得到某个资源后才能继续运行下去时,整个程序将被挂起,这种情况就叫做死锁。
- Java语言高编——多线程(并发)
- Java语言高编——异常
- Java语言高编——面向对象-继承
- Java语言高编——面向对象-多态
- Java语言高编——面向对象-接口
- Java语言高编——面向对象-抽象类
- Java语言高编——集合框架
- Java语言高编——内部类
- Java语言高编——计算机网络协议
- Java语言高编——IO流(文件操作)
- 高并发Java(2):多线程基础
- Java多线程高并发学习笔记(一)——Thread&Runnable
- Java多线程和高并发
- JAVA高并发之一----多线程
- java多线程和高并发
- JAVA多线程、高并发梳理
- 【多线程】Java高并发基础
- (五十二)高并发服务器——多线程模型
- Java是面向对象语言
- 加载Xib两种方法
- Go编程基础—常量
- fastdfs 错误问题
- android UiAutomator截图保存用例信息的方法
- Java语言高编——多线程(并发)
- Hibernate环境配置以及使用方法
- vim常用命令总结
- 葵花宝典 二十一 数据库sql
- linux系统介绍
- Linux中字符串截取方法
- 冒泡排序
- 南阳oj 题目91 阶乘之和
- 【应用实例】单片机PM2.5空气监测仪--攀藤G5激光PM2.5传感器