首页 > 代码库 > 跟着刚哥梳理java知识点——多线程(十六)

跟着刚哥梳理java知识点——多线程(十六)

创建多线程
第一种方式
① 继承:继承Thread。
② 重写:重写Thread类的run()方法
③ 创建:创建一个子类的对象
④ 调用:调用线程的start()方法,启动此线程,调用run()方法

 1 class Work extends Thread{ //① 继承 2   @Override 3   //② 重写 4   public void run() { 5     for (int i = 1 ;i < 5; i++) { 6       System.out.println(Thread.currentThread().getName()+":"+i); 7     } 8   } 9 }10 public static void main(String[] args) {11   //③ 创建12   Work work = new Work();13   //④ 调用14   work.start();15   for (int i = 1 ;i < 5; i++) {16     System.out.println(Thread.currentThread().getName()+":"+i);17   }18 }

输出结果:

main:1main:2main:3main:4Thread-0:1Thread-0:2Thread-0:3Thread-0:4

思考:把上面的start修改成run,想想会有什么结果呢?

main:1main:2main:3main:4main:1main:2main:3main:4

走了两遍的main。因为Start是启动线程,run只是正常的调用了一下方法,和多线程没关系。


第二种方法
① 实现接口:实现Runnable接口的类
② 实现抽象方法:实现接口的run的方法
③ 创建对象:创建一个Runnable接口实现类的对象
④ 放入构造器:将此对象作为形参传递给Thread的构造器,创建Thread对象
⑤ 启动线程:启动这个线程

 1 class Work implements Runnable{ //① 实现接口 2   //② 实现抽象方法 3   public void run() { 4     for (int i = 11 ;i < 100; i++) { 5       System.out.println(Thread.currentThread().getName()+":"+i); 6     } 7   } 8 } 9 10 public static void main(String[] args) {11   //③ 创建对象12   Work work = new Work();13   //④ 放入构造器14   Thread t1 = new Thread(work);15   //⑤ 启动线程16   t1.start();17   for (int i = 11 ;i < 100; i++) {18     System.out.println(Thread.currentThread().getName()+":"+i);19   }20 }

 

Thread(类) VS Runnable(接口)
① Runnable避免了java类的单继承局限性,接口可以多继承。
② 如果多个线程操作同一份资源更适合使用Runnable的方式


线程Thread的常用方法:
① start():启动线程并执行相应的run()方法
② run():将子线程要执行的代码放入run()方法
③ currentThread():静态的,调取当前的线程
    √ getName():获取此线程的名字
   例如:Thread.currentThread().getName()

    √ setName():设置线程的名字

④ yield():强制释放当前cpu执行权,(例如子线程和主线程都循环输出100次的数字,当主线程%10==0的时候,就调用主线程yield方法Thread.currentThread().yield(),强制主线程释放CPU执行权)需要说明的是释放线程的CPU执行权不代表其他线程就一定能抢到CPU的执行权。也可能释放的线程再次抢到资源。
⑤ join():在A线程中调用B线程join(参与进来的意思)方法,表示当执行到此方法,A线程停止执行,B执行完毕后,A再执行。
⑥ sleep():显式的让当前线程睡眠1毫秒

设置线程的优先级:优先级高只能说明抢到的几率高,不代表一定先完成
① getPriority():获取线程的优先级
② setPriority():设置线程的优先级
一共是10个等级.默认是等级5,Thread里的属性就是等级级别
Thread属性:
√ MAX_PRIORITY:最高的线程优先级
√ MIN_PRIORITY:最低的线程优先级
√ NORM_PRIORITY:默认的线程优先级
线程分为两类
①守护线程
   用来服务用户的,垃圾回收就是一个典型的守护线程
   若JVM都是守护线程,当前JVM将退出
②用户线程
   用户自己创建的线程
   用户线程-->守护线程: 
   通过在start()方法前调用thread.setDaemon(True)就可以

线程的生命周期:枚举status代表了状态

技术分享

1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
   (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
   (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
   (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程安全性
原因:
由于一个线程在操作共享数据过程中未执行完的情况下,另外的线程参与进来,
导致共享数据存在安全问题。
共享数据:多个线程共共同操作同一个数据(变量)

解决:
必须让一个线程操作共享数据完毕之后,其他线程才有机会共享数据的操作

java如何解决的呢?
方式一:同步代码块
synchronized(同步监视器){
//需要同步的代码
}
同步块包住谁呢?谁操作共享数据就包谁
注意:在实现的方式中,可以使用this充当锁,但是在继承的方式中,慎用this

方式二:同步方法
同步方法的锁:this

public synchronize void 方法(){}

单例模式:线程安全

 1 class Singleton{ 2   private Singleton(){ 3    4   } 5   private static Singleton instance = null; 6   public static Singleton getInstance(){ 7   if(instance == null){ 8     synchronize(Singleton.calss){ 9       if(instance == null){10         instance = new Singleton();11       }12     }13   }14   return instance;15 } 

 

面试题:
银行有一个账号,有两个储户分别向一个账户存入3000元,每次存1000,存3次
,每次存完后打印账户余额

分析:
共享资源是什么?显然是一个账号。
是否需要用多线程?显然是用的,因为有两个储户

 1 class Account { 2   double balance = 0; 3   public synchronized void cunqian(double crm){ 4     balance += crm; 5     System.out.println(Thread.currentThread().getName() + ":" + balance); 6   } 7 } 8 class Customer implements Runnable{ 9   Account account;10   public Customer(Account account) {11     this.account = account;12   }13   @Override14   public void run() {15     for (int i = 0; i < 3; i++) {16       account.cunqian(1000);17     }18   }19 }20 public static void main(String[] args) throws Exception {21   Account account = new Account();22   Customer customer = new Customer(account);23   Thread t1 = new Thread(customer);24   t1.start();25   Thread t2 = new Thread(customer);26   t2.start();27   t1.setName("储户1");28   t2.setName("储户2");29 }

上面代码需要需要的是:由于是两个类,一定要保证共享资源类千万不要被多次实例化
所以一定要让第一个类实例化完成后当成形参出入到第二个中构造(看红色标记部分)


线程通信:
① wait():令当前线程挂起并放弃CPU、同步资源。让别的线程可访问并修改共享资源,而当前前程排队等候再次对资源的访问
② notify():唤醒正在排队等候同步资源的线程中优先级最高的锁
③ notifyAll();唤醒所有正在排队的等待的所有线程结束等待

跟着刚哥梳理java知识点——多线程(十六)