Java多线程:java语言的一个优势就是处理多线程简单
在一个操作系统中会同时运行多个任务(程序),例如:QQ、微信等
1.在操作系统中同时运行的每一个任务都是一个进程
2.对于一个进程(程序)而言,在程序内部也会同时运行多个任务,那个每一个任务称为一个线程
线程的运行都是并发执行的,所谓的并发:宏观上所有的线程都是同时执行的,但是微观上所有的线程都是"走走停停"
线程的调度:
将CPU的时间划分为若干个时间片段,尽可能的均匀分配给每一个线程,获得CPU时间片段的线程将得以CPU执行
创建线程的两种方式:Thread类 线程类
其每一个实例表示一个可以并发的线程(匿名内部类)
使用线程的注意事项:
对于线程调度而言,分配的时间片段长短,具体分配给哪一个线程时间片段,对于程序而言,都是不可控制的
* 使用多线程不考虑执行的先后顺序问题
* 执行没有先后顺序的是异步运行(并发运行)--登录
* 执行有先后顺序的是同步运行--银行取钱
第一种创建线程的方式:继承Thread类
第一种创建线程的方式存在弊端:
* 线程与线程要干的事情(任务)耦合在一起
* --启动线程是调用start方法,自动执行run方法
*继承只能单继承
第二种创建线程的方式:实现Runnable接口
--将任务和线程解耦
线程在执行某段逻辑时可以发生阻塞现象(控制自己不被执行)
sleep阻塞:
该阻塞可以指定阻塞时间,并在线程阻塞了该时间后自动返回runnable等待方法,Thread提供了一个静态方法sleep()用于睡眠阻塞
线程阻塞在以下情况可以停止:
1、run方法正常执行完毕
2、run方法执行过程中抛出了一个未捕获的异常
进程的停止:
当一个进程中所有前台线程停止后,该进程结束
前台线程和后台线程
后台线程的特点:用法与前台线程无异,只是当一个进程中所有前台线程都结束后,
无论后台线程是否还在运行中都被强制结束,从而使得进程结束,程序退出
后台线程:也称为守护线程,或者精灵线程---lol开挂刷野时间盒
设置后台线程
设置后台线程方法要在该线程被调用start()之前调用
zhou.setDaemon(true);
在运行我们的程序时,操作系统会启动一个进程来运行JVM,JVM运行后会创建第一个前台
线程来运行我们的程序的main方法,同时也会创建一个后台线程来运行GC(垃圾回收机制)
线程的优先级:1-10(1最低、10最高)
优先级越高的线程被分配时间片段的机会就越多,那么CPU执行的机会就越多
线程安全:
多线程并发访问同一资源时,会产生线程安全问题
解决办法:并发即异步---将异步操作变成同步操作
1、多线程并发读写同一临界资源会发生“线程安全并发问题”
如果保证多线程同步访问临界资源,就可以解决安全问题。
2、常见的临界资源:
多线程共享的实例变量、静态的公共变量
3、异步:各干各的 执行没有先后顺序 AJax
同步:你干完我再干 执行有先后顺序
4、同步锁:关键字synchronized----重要!!!
synchronized可以修饰方法,当一个方法被修饰后,这个方法就是同步方法,同一刻只有一个线程访问该方法
synchronized块
当一个方法被修饰后,该方法变成同步方法,虽然保证了代码的执行安全,但是效率
低下,我们实际上只需要将方法需要的同步片段加锁,这样可以缩小同步范围,从而
提高代码的运行效率
语法:
synchronized(同步监视器){
需要同步的代码片段
}
同步监视器:就是一个对象,任何对象都可以,可以new Object()
但要保证一点,多线程看到的应该是"同一个"对象,通常情况下使用this
非线程安全 线程安全
StringBuilder StringBuffer
ArrayList Vector
HashMap HashTable
HashSet ......
对于集合Collection和Map而言
Collections类提供了可以将给定的集合转换为线程安全的集合方法
list = Collections.synchronizedList(list);
线程的协调工作:
download.join(); //先执行download线程
wait-notify
使用wait()和notify()完成线程的协同工作
* wait()阻塞会在以下两种情况被解除
* 1.当download线程结束
* 2.当调用了download的notify()
* ---属于Object方法
synchronized(object){
object.wait();
synchronized(object){
//通知object身上的等待的线程解除阻塞
object.notify();
}
Day05
总结--创建线程的方式:
1、继承Thread类
实体类继承Thread类,覆盖run()方法,提供并发运行的过程
创建这个类的实例 使用start()启动线程
2、实现Runnable接口
3、使用内部类/匿名内部类创建线程
线程的状态
1、new 新建状态,还未启动
2、Runnable 等待状态,可以运行状态(就绪)
3、Ruuning 正在运行的状态,该线程已经获得了CPU
a.假设线程获取了CPU,则进入Running状态,开始执行线程体
b.假设系统只有一个CPU那么在任意时间点,只有一个线程属于Running
如果是双核,那么在同一时间点,那么会有两条线程处于Running,但是当线程数大于处理器,依然会是多条线程在同一CPU轮换执行
c.当一条线程开始运行的时候,如果不是一瞬间完成,那么它不可能一直处于
Running状态,线程会在执行过程中被中断,目的是为了让其他线程获得执行机会,像这样的线程调度的策略取决于底层平台,对于抢占式策略的平台来说,
系统会给每个可执行的线程一小段时间来处理任务,当该时间片段用完了,系统会剥夺线程所占资源(CPU),让其他线程获得运行机会
4、block 阻塞或者挂起状态
a.以下情况会发生阻塞状态
1.线程使用了sleep()
2.线程调用了阻塞式IO方法(比如控制台输入方法),在该方法放回前,该线程被阻塞
3.join wait-notify
b.当正在执行的线程被阻塞时,其他线程就获得机会,阻塞结束的时候,该线程
进入Runnable等待状态,而非直接进入Running状态
5、dead 死亡状态
run()执行结束,该线程进入死亡状态,不要试图对一个已经死亡的线程调用
start(),死亡后不能再次作为线程执行,会抛出异常
线程的状态管理:
1、Thread.sleep(times)
使当前线程从Running放弃处理器进入block状态,休眠times毫秒,再返回Runnable状态,如果其他线程打断了当前线程的Block(sleep)
就会发生中断异常InterruptedException
2、Thread.yield()
让出CPU,当线程让出处理器(离开Running),进入Runnable状态
线程的常用属性和方法:
1.线程的优先级
setPriorty
2.后台线程(守护线程)
线程对象引用名.setDaemon(true)
3.获取线程的名字
getName()
4.获取当前线程
Thread.currentThread()
5.sleep状态的打断和唤醒
Thread.sleep(times)
引用名.interrupt() 被唤醒的线程会出现中断异常
线程池:Java1.5提供了并发包concurrent
Executors是工厂,包含工厂用于创建Executor接口的实例
Executor threadPools=Executors.newFixedThreadPool(2);
Process p1=new Process();
threadPools.execute(p1);//执行
创建线程的两张方式:
package day04;
//第一种创建线程的方式:继承Thread类
public class ThreadDemo01 {
public static void main(String[] args) {
Thread t1=new Person1();
Thread t2=new Person2();
//启动线程是调用start方法,自动执行run方法
t1.start();
t2.start();
/*
* 使用多线程不考虑执行的先后顺序问题
* 执行没有先后顺序的是异步运行(并发运行)--登录
* 执行有先后顺序的是同步运行--银行取钱
*/
}
/*
* 第一种创建线程的方式存在弊端:
* 线程与线程要干的事情(任务)耦合在一起
* --启动线程是调用start方法,自动执行run方法
*/
}
class Person1 extends Thread{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你是谁啊");
}
}
}
class Person2 extends Thread{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("我是收电费的");
}
}
}
package day04;
//第二种创建线程的方式:实现Runnable接口
//将任务和线程解耦
public class ThreadDemo02 {
public static void main(String[] args) {
//创建线程时再将任务指派
Thread t1=new Thread(new PersonOne());
Thread t2=new Thread(new PersonTwo());
t1.start();
t2.start();
}
}
class PersonOne implements Runnable{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你是谁啊");
}
}
}
class PersonTwo implements Runnable{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("查水表的");
}
}
}
多线程Join:
package day04;
//线程的协调工作
public class JoinThreadDemo {
//图片是否下载完毕
public static boolean isFinish = false;
public static void main(String[] args) {
//下载图片的线程
final Thread download = new Thread(){
public void run(){
System.out.println("download:开始下载图片");
for(int i=0;i<=100;i++){
System.out.println("download:已完成"+i+"%");
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("download:图片下载完毕");
isFinish = true;//表示图片下载完毕
}
};
//显示图片的线程
Thread showImg = new Thread(){
public void run(){
System.out.println("showImg:准备显示图片");
//等待下载线程工作结束后,再执行下面的代码
try {
//先执行download
download.join();
} catch (Exception e) {
e.printStackTrace();
}
if(!isFinish){
throw new RuntimeException("图片没有下载完毕");
}
System.out.println("showImg:图片已经显示了");
}
};
showImg.start();
download.start();
}
}
多线程wait()...notify()
package day05;
//使用wait()和notify()完成线程的协同工作
public class WaitAndNotifyDemo {
public static boolean isFinish = false;
public static Object object=new Object();
public static void main(String[] args) {
//下载图片的线程
final Thread download = new Thread(){
public void run(){
System.out.println("download:开始下载图片");
for(int i=0;i<=100;i++){
System.out.println("download:已完成"+i+"%");
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("download:图片下载完毕");
isFinish = true;//表示图片下载完毕
synchronized(object){
//通知object身上的等待的线程解除阻塞
object.notify();
}
}
};
//显示图片的线程
Thread showImg = new Thread(){
public void run(){
System.out.println("showImg:准备显示图片");
//等待下载线程工作结束后,再执行下面的代码
try {
/*
* wait()阻塞会在以下两种情况被解除
* 1.当download线程结束
* 2.当调用了download的notify()
* ---属于Object方法
*/
synchronized(object){
object.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
if(!isFinish){
throw new RuntimeException("图片没有下载完毕");
}
System.out.println("showImg:图片已经显示了");
}
};
download.start();
showImg.start();
}
}