目录
多线程
静态代理设计模式
之前讲通过Runnable接口启动多线程的操作,需要借助thread类的对象,这个thread类可以成为静态代理。
静态代理:这个类是提前写好的,可以直接拿来用。
动态代理: 这个类是在运行过程中,动态构建出来的,可以理解为临时构建出来的。
/*
* 静态代理
* 公共接口:
* 1、真实角色
* 2、代理角色
* */
public class StaticProxy {
public static void main(String[] args) {
new WeddingCompany(new You()).happyMarry();
}
}
interface Marry{
void happyMarry();
}
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("我和小机智奔月啦~");
}
}
//代理角色
class WeddingCompany implements Marry{
//真实角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void happyMarry() {
ready();
this.target.happyMarry();
after();
}
private void ready(){
System.out.println("准备好火箭");
}
private void after(){
System.out.println("运送物质去月球");
}
}
Lambda表达式简化线程
/*
* Lambda表达式简化只使用一次的线程使用
* */
//好处,LambdaThread使用的时候才会编译
public class LambdaThread{
//静态内部类
static class Test implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("一边听歌1");
}
}
}
public static void main(String[] args) {
//局部内部类
class Test2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("一边听歌2");
}
}
}
new Thread(new Test()).start();
new Thread(new Test2()).start();
//匿名内部类,必须借助接口或者父类
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("一边听歌3");
}
}
}).start();
//匿名内部类简化 lambda (只能是一个方法)
new Thread(()-> {
for (int i = 0; i < 3; i++) {
System.out.println("一边听歌4");
}
}).start();
for(int i=0;i<3;i++){
System.out.println("一边coding");
}
}
}
new Thread(()-> {System.out.println("一边听歌4"); }).start();
线程状态
一个线程对象在整个生命周期内,需要经历5个状态。
新生状态(new):用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。
就绪状态(Runnable):处于就绪状态的线程已经具备了运行条件,、处于“线程就绪队列”,等待系统为其分配CPU。当系统选定一个等待执行的Thread对象后,就绪状态就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
线程进入就绪状态的四种原因:
1. 新建线程:调用start()方法,进入就绪状态;
2. 阻塞线程:阻塞解除,进入就绪状态;
3. 运行线程:调用yield()方法,直接进入就绪状态;
4. 运行线程:JVM将CPU资源从本线程切换到其他线程。
运行状态(Running): 在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。
阻塞状态(Blocked):阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。有4种原因会导致阻塞:
1. 执行sleep()方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。
2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。
3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。
4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。
死亡状态(Terminated):
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它run()方法内的全部工作; 另一个是线程被强制终止。
注意:
(1)运行状态被暂停后,再开启是要先进入就绪状态。
(2)当一个线程进入死亡状态以后,就不能再回到其它状态了。
终止线程的典型方式
通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行。
/*
* 终止线程
* 1、线程正常执行完毕-次数
* 2、外部干涉-加入标识
* */
public class TerminateThread implements Runnable{
//加入标识,标记线程体是否可以运行
private boolean flag = true;
private String name;
public TerminateThread(String name) {
this.name = name;
}
@Override
public void run() {
int i=0;
//关联标识,ture运行,false停止
while(flag){
System.out.println(name+"--"+i++);
}
}
public void treminate(){
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
TerminateThread tt = new TerminateThread("gaosan");
new Thread(tt).start();
for(int i=0;i<=10;i++){
sleep(1);
if(i==8){
tt.treminate(); //外部控制线程中止
System.out.println("线程中止");
}
System.out.println(i);
}
}
}
暂停线程执行
常用的方法有sleep()和yield()方法,这两个方法的区别是:
1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。
2. yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权。
/*
* yield 让出CPU的调度
* */
public class YieldDemo01 {
public static void main(String[] args) {
Myyield my = new Myyield();
new Thread(my,"a").start();
new Thread(my,"b").start();
}
}
class Myyield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--start");
Thread.yield(); //礼让
System.out.println(Thread.currentThread().getName()+"--end");
}
}
线程的联合
线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。
所以线程的联合可以理解为插队线程。
public class BlockedJoin {
public static void main(String[] args) {
System.out.println("父亲和儿子买烟的故事");
Thread father = new Thread(new Father());
father.start();
}
}
class Father extends Thread{
@Override
public void run() {
System.out.println("父亲想抽烟,但是没有烟了");
System.out.println("让儿子去买软中华");
Thread t = new Thread(new Son());
t.start();
try{
t.join();
System.out.println("父亲开始抽烟");
}catch (InterruptedException e){
e.printStackTrace();
System.out.println("儿子没有回来");
}
}
}
class Son extends Thread{
@Override
public void run() {
System.out.println("接过父亲的钱出去了");
System.out.println("买烟的路上时间");
for(int i=1;i<10;i++){
System.out.println(i+"秒过去了");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("儿子手拿一包烟回来了");
}
}
运行结果:
t.join();
线程的常用方法
public class InfoTest {
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().isAlive());
//设置名称:真实角色+代理角色
MyInfo info = new MyInfo("gaosan");
Thread t = new Thread(info);
t.setName("高三");
t.start();
t.sleep(100);
System.out.println(t.isAlive());
}
}
class MyInfo implements Runnable{
private String name;
public MyInfo(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----"+name);
}
}
线程的优先级
1. 处于就绪状态的线程,会进入“就绪队列”等待JVM来挑选。
2. 线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。
3. 使用下列方法获得或设置线程对象的优先级。
int getPriority();
void setPriority(int newPriority);
注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。
public class TestThread {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread(), "t1");
Thread t2 = new Thread(new MyThread(), "t2");
t1.setPriority(1);
t2.setPriority(10);
t1.start();
t2.start();
}
}
守护线程
线程分为用户线程和守护线程。守护线程是为用户线程服务的,当用户线程执行完成后,无论守护线程有没有执行完,JVM都会关闭。默认线程都是用户线程。
/*
* 守护线程
* */
public class DaemonTest {
public static void main(String[] args) {
God god = new God();
People you = new People();
Thread t = new Thread(god);
t.setDaemon(true); //将用户线程改为守护线程
t.start();
new Thread(you).start();
}
}
class People extends Thread{
@Override
public void run() {
for(int i=0;i<=10;i++){
System.out.println("happy");
}
}
}
class God extends Thread{
@Override
public void run() {
while(true){
System.out.println("God bless you");
}
}
}
t.setDaemon(true); //将用户线程改为守护线程