跟着刚哥梳理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:1
main:2
main:3
main:4
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4

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

main:1
main:2
main:3
main:4
main:1
main:2
main:3
main: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   @Override
14   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();唤醒所有正在排队的等待的所有线程结束等待

时间: 2024-10-12 20:12:41

跟着刚哥梳理java知识点——多线程(十六)的相关文章

跟着刚哥梳理java知识点——泛型和注解(十四)

enum Season{ SPRING("spring","春暖花开"), SUMMER("summer","夏日炎炎"), AUTUMN("autumn","秋高气爽"), WINTER("winter","白雪皑皑"); } 其实上面等等同于: class Season{ Public static final Season SPRING =

跟着刚哥梳理java知识点——运算符(五)

运算符:是一种特殊的符号,用以表示数据的运算.赋值和比较. 1.算数运算符(+.-.*./.%.++.--) a)除: int i = 12; int j = i / 5; //2 double d1 = i / 5; //2.0 double d2 = (double)i/5; //2.4 double d3 = i/5.0; //2.4 b)前++和后++区别 前++:先自增一赋值给自己,后做运算赋值 后++:先做运算赋值,后自增一赋值给自己 int a = 10; int b = ++a;

跟着刚哥梳理java知识点——基本数据类型(三)

1.8种基本数据类型 1)4种整数类型(byte.short.int.long) [知识点] 类型 存储空间 数值范围 byte 1字节=8位 -128-127 short 2字节 -2的15次方-2的15次方-1 int 4字节 -2的31次方-2的31次方-1 long 8字节 -2的63次方-2的63次方-1 a)整数的默认类型是int b)以上4种类型,只有long型必须要在末位加上L或者l,其它直接赋值数值即可. byte b1 = 12; byte b2 = 128; //报错 sh

跟着刚哥梳理java知识点——流程控制(六)

分支结构(if…else .switch) 1.if else 语句格式 if(条件表达式){ 执行代码块; } else if(条件表达式){ 执行代码块; } else{ 执行代码块; } 2.switch语句 switch(变量){ case 常量值1: 语句1; break; case 常量值2: 语句2; break; case 常量值3: case 常量值4: case 常量值5: 语句5; break; default: 语句; break; } [知识点]: a)switch:在

跟着刚哥梳理java知识点——变量之间的类型转换(四)

变量之间的类型转换主要包括自动类型转换和强制类型转换. 1.自动类型转换:当容量小的数据类型与容量大的数据类型做运算时,容量小的会自动的转换成容量大的类型. [知识点]: a)char,byte,short ---> int ---> long ---> float ---> double ---> String char c = 'a'; short s = 12; byte b = 125; //char.short和byte之间运算全部自动转换成int int s1 =

跟着刚哥梳理java知识点——注释(二)

1.单行注释 // //这是main方法,程序的入口 public static void main(String[] args) { //输出语句 System.out.println("Hello World"); } 2.多行注释 /* */ /* * print和println区别 */ 知识点:多行注释不能再嵌套多行注释 3.文档注释 /**     */ 知识点:常见注释标签 1)@author    用在类中,作者. 2)@version   对类的说明,标明该类的版本.

跟着刚哥梳理java知识点——反射和代理(十七)

反射机制是什么?反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有的属性和方法:对于任意一个对象,都能够调用他的一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 反射机制能做什么? 反射机制主要提供以下功能 √ 在运行时判断任意一个对象所属的类 √ 在运行时构造任意一个类的对象 √ 在运行时判断任意一个类所具有的的属性和方法 √ 在运行时调用一个对象的方法 √ 生成动态代理 通过一个对象获得完整的包名和类名 package com.hzg;

跟着刚哥学习Spring框架--JDBC(六)

Spring的JDBC框架 Spring JDBC提供了一套JDBC抽象框架,用于简化JDBC开发. Spring主要提供JDBC模板方式.关系数据库对象化方式.SimpleJdbc方式.事务管理来简化JDBC编程 Spring提供了3个模板类: JdbcTemplate:Spring里最基本的JDBC模板,利用JDBC和简单的索引参数查询提供对数据库的简单访问. NamedParameterJdbcTemplate:能够在执行查询时把值绑定到SQL里的命名参数,而不是使用索引参数. Simpl

跟着刚哥学习Spring框架--通过XML方式配置Bean(三)

Spring配置Bean有两种形式(XML和注解) 今天我们学习通过XML方式配置Bean 1. Bean的配置方式 通过全类名(反射)的方式   √ id:标识容器中的bean.id唯一. √ class:bean的全类名,通过反射的方式在IOC容器中创建Bean,所以要求Bean中必须有无参的构造器 2.依赖注入的方式 1)属性注入:通过setter方法注入Bean的属性值或依赖的对象 属性注入使用<Property>元素,使用name指定Bean的属性名称,使用value指定Bean的属