第九章 Thread 多线程
*线程创建
ThreadInfo:常用方法
t.getId();
t.getName();
t.currentThread();
t.isAlive();
t.isDaemon();
t.getPriority();
t.isInterrupted();
t.interrupt();
t.join();
t.sleep(long ms);
Thread.setDaemon(true);
synchronized(同步监视器对象){}
*线程有两种创建方式:
*1:继承Thread并重写run方法
*第一种创建方式存在两种设计不足:
*1:继承冲突
* 由于java是单继承的,在实际开发中为了可以复用方法,我们通常会继承某个超类,但如果此时当前类需要线程的特性时就会导致无法同时继承两个类
*2:继承线程并重写run方法来定义线程任务会导致线程与任务存在一个必然的耦合关系,这不利于线程的重用
*
*通俗理解:
*不足1:java单继承制导致类在继承超类和线程之间的选择纠结
*不足2:线程和任务之间的高耦合性,导致线程只能干一件事,复用性差
*优点:创建方式简单,适合匿名内部类快速创建
* 线程启动要调用start方法,而不是直接调用run方法
* 当start方法执行后,线程纳入线程调度,一旦被分配CPU时间片,线程会自动执行run方法
* 第一种创建方式
* 创建类并继承Thread,重写run方法,在run方法中定义线程任务
* 在main方法中创建线程并调用start方法启动线程进入Ruannable状态,等待cpu分配时间片
* 第二种创建方式
* 创建类并实现Runnable接口,在类中中写run方法,单独定义线程任务;
class MyRunnable1 implements Runnable{
public void run() { 线程任务 }
* 在main方法中创建线程并指派任务
MyRunnable r1 = new MyRunnable();
Thread t = new Thread(r1);
t.start();
* 调用start方法启动线程进入Ruannable状态,等待cpu分配时间片
* 使用匿名内部类完成创建线程的方式
Thread t1= new Thread() { public void run() { for(int i=0;i<1000;i++) { System.out.println("你是谁?"); } } };
ThreadInfo
获取线程相关信息的方法(如线程的ID,名称,优先级),不具备操作功能
t.getPriority();
* 线程优先级,最小优先级1,最高优先级10,默认5
* 理论上优先级越高的线程获取cpu时间片的次数越多
max.setPriority(Thread.MIN_PRIORITY); nom.setPriority(Thread.NORM_PRIORITY); min.setPriority(Thread.MIN_PRIORITY); t.currentThread(); Thread main = Thread.currentThread(); t.getName(); String name = main.getName();//线程名字 t.getId(); long id = main.getId();//线程的唯一标识 t.getPriority(); int priority = main.getPriority();//线程的优先级,默认为5 t.isAlive(); boolean isAlive = main.isAlive();//显示线程是否处于活动状态(任务是否执行结束) t.isDaemon(); boolean isDaemon = main.isDaemon();//是否为守护线程 t.isInterrupted(); boolean isInterrupted = main.isInterrupted();//是否被中断
* 线程提供了一个静态方法
* static Thread.currentThread();
* 该方法可以获取运行这个方法的线程
获取运行main方法的线程
Thread main = Thread.currentThread();
System.out.println("运行main方法的线程是:"+main);
运行main方法的线程是:Thread[main,5,main],
第一个参数是线程名称,main也叫做主线程,第二个参数是优先级,默认5
我们平时创建线程时,名字一般是系统默认给的,如:Thread-0,Thread-1,Thread-2,……
Thread.sleep();
* 线程提供了一个静态方法
*static void sleep(long ms);
* 该方法可以让运行这个方法的线程进入阻塞状态指定毫秒
Thread.interruput();
* 当调用一个线程的Interruput方法后目的是中断该线程
* 但若该线程正处于sleep状态时,则会抛出中断异常,此时并不是直接把线程中断,而只是打断了其阻塞状态
*JDK1.7及之前版本要求:一个方法的局部内部类当中想引用这个方法的其他局部变量,这个变量就必须是final的
Thread.setDaemon();
* Daemon 守护线程,也叫后台线程,使用上与普通线程(也称前台线程)并无区别
* 但是在结束时机上有一点不同,当一个进程中所有前台线程结束时,进程就会结束,
* 这时所有还在运行的守护线程都会被强制终止
* 设置守护线程必须在调用start启动线程之前进行
t1.start();
t2.setDaemon(true);
t2.start();
Thread.yield();//主动让出cpu时间片
t.join();
* 线程提供了一个方法:join
* 该方法可以协调线程之间的同步运行关系,它可以使一个线程在另一个线程“后面”等待,直到该线程运行完毕再继续运行
*
* 同步运行:执行有先后顺序
* 异步运行:各自执行各自的,多线程是异步运行的
synchronized(同步监视器对象){}
【1】
* 当多个线程并发操作同一资源时,由于线程切换实际不确定,会导致操作资源的代码执行顺序未按照设计要求执行,导致出现操作混乱,严重时可能导致系统崩溃
* 同步方法,即:多个线程不能同时在方法内部执行
* 将多个线程异步操作一个资源改为同步排队操作就不会出现并发安全问题了;
* 在方法上使用同步监视器synchronized时,同步监视器对象就是当前方法所属对象,即:this
* synchronized 安全 效率 二者有得有舍
* 有效的缩小同步范围,可以尽可能的提升效率
【2】synchronized块
有效的缩小同步范围可以保证在并发安全的情况下提高并发的效率;
同步块语法:
synchronized(同步监视器对象){需要同步运行的代码片段}
* 若希望多个线程同步运行同步块中的代码,必须保证多个线程看到的同步监视器对象是同一个;
* 同步监视器对象可以是java中任何对象,结合实际情况自行挑选,只要保证多个并发线程看到的是同一个即可
【3】 静态方法使用synchronized修饰后,一定具有同步效果;(静态方法属于类,只有一份,加synchronized锁后,多线程调也一定是同步效果)
【4】synchronized互斥锁
当synchronized修饰不同代码片段,但是同步监视器对象是同一个时,那么这些代码片段就是互斥的,多个线程不能同时访问这些代码
(同步监视器对象的选择:因为加synchronized是为了避免多个线程同时操作同一个资源造成的线程不安全问题,所以同步监视器对象可选择该资源对象)