Thread_线程类

1.线程和进程的区别
 进程:由系统资源分配和调度的最小单元,而线程依赖于进程存在,程序执行的最小单位!
  • 如果创建线程----必须有进程----->创建系统资源

  • Java语言不能够创建系统资源, 借助于底层语言C语言来操作

  • Java提供类Thread类

  • 借助于Thread类如何实现 线程的创建?

  • 并发:在一个时间点同时发生

  • 并行:在一个时间段内同时发生

  • 开发步骤:

    ​ 1)自定义一个类 继承自 Thread(线程 是程序中的执行线程。Java 虚拟机允许应

    ​ 用程序并发地运行多个执行线程。 )

    ​ 2)该子类应重写 Thread 类的 run 方法

    ​ 3)创建当前子类对象,然后启动线程:start()而非run方法()

  • 启动线程:使用start 方法

2线程的成员方法:
线程实现方式1

​ 设置线程名称:public final void setName(String name)

​ 获取线程名称:public final String getName()

​ public final void join()

​ throws InterruptedException :等待该线程终止

public static void main(String[] args) {

        //创建两个线程
        ThreadJoin tj1 = new ThreadJoin() ;
        ThreadJoin tj2 = new ThreadJoin() ;
        ThreadJoin tj3 = new ThreadJoin() ;

        //设置名称
        tj1.setName("令狐冲") ;
        tj2.setName("东方不败") ;
        tj3.setName("任盈盈") ;

        tj1.start();
        //try...catch...捕获异常
        //throws:抛出异常:方法声明上
        try {
            tj1.join(); //等待tj1终止
        } catch (InterruptedException e) {
            e.printStackTrace();//日志: 跟踪堆栈,将异常信息打印在控制台上
        }
        tj2.start();
        tj3.start();
    }

​ public static void yield():暂停当前正在执行的线程,执行其他线程!

3.Thread类的常量字段表
  • public static final int MAX_PRIORITY 10
  • public static final int MIN_PRIORITY 1
  • public static final int NORM_PRIORITY 5 :默认值
  • public final void setPriority(int newPriority)设置优先级
  • public final int getPriority()
  • 优先级越大的线程,抢占CPU的执行权(cpu一点点时间片,可以高效的进行切换)的资格大
  • 优先级越小的线程:抢占CPU的执行权越小!
  • 线程的执行具有随机性!
public static void main(String[] args) {

        //创建三个线程类对象
        ThreadPriority tp1 = new ThreadPriority() ;
        ThreadPriority tp2 = new ThreadPriority() ;
        ThreadPriority tp3 = new ThreadPriority() ;

        tp1.setName("高圆圆");
        tp2.setName("赵又廷");
        tp3.setName("张佳宁");

        //设置优先级
        tp1.setPriority(10);
        tp2.setPriority(1);
        System.out.println(tp3.getPriority());//5

        //启动线程
        tp1.start();
        tp2.start();
        tp3.start();
    }
1.成员实现方式2

​ Runable(实现关系)

​ 步骤:

​ (1)自定义一个类,实现Runable接口,重写run方法

​ (2)在用户线程(main)创建当前“资源类”对象

​ 然后创建Thread类对象,将“资源类”对象作为参数传递

​ public Thread(Runable target ,String name)

​ (3)分别启动线程

​ //thread类的静态功能

​ public static Thread currenThread()表示正在执行的线程对象的引用

电影院有三个窗口,总共100张票,使用多线程模拟!
 *
 *  实现方式2
 *        使用Runnable接口的方式
 *
 *
 * 第二种方式能够体现  "资源共享"
 *
 * 程序出现:
 *      1)一个张票可能被卖多次(出现同票)
 *              线程的执行具有随机性(原子性操作:最简单,最基本的操作语句:-							-,++...)
 *      2)出现了负票
 *             线程的延迟性导致(加入睡眠100毫秒)
 *      现在的程序存在 "多线程安全问题"
 */
public class SellTicketDemo {
    public static void main(String[] args) {

        //创建资源共享类对象
        SellTicket st = new SellTicket() ;
        //创建多个线程类对象,将资源共享类作为参数传递
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

2.synchronized:关键字---- 内置语言实现的(jvm来实现锁定操作: 锁的释放:自动释放)
  •  可以是通过代码块(代码中),可以在方法上使用(同步方法)
    
  •      synchronized同步锁---属于"悲观锁"
    
  •                  悲观锁:自己线程本身在执行的时候,其他线程不能进入同步代码块中,其他线程不能进入
    
  •                  修改数据,保证数据的安全性!  (针对频繁的写入操作)
    
public class SellTicket implements Runnable {
    public static int tickets = 100 ;//100张票

    private Demo demo = new Demo();
   // private Object obj = new Object() ;


    //t1,t2,t3
    @Override
    public void run() {
        //为了模拟一直有票
        while(true){
        //t1,t2,t3
            //加入同步代码块
            //把它理解为:门的开/关
//            synchronized(new Object()){ //三个线程有自己的锁对象


            //t1进来,t2和t3没有办法进入的
           // synchronized(obj){ //一个锁对象
            //锁对象可以是任意的Java类对象
            synchronized(demo){ //一个锁对象
                if(tickets>0){  //t1进来
                    //睡眠100毫秒
                    //为了模拟网络延迟
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+
                            "正在第"+(tickets--)+"张票");
                }
            }
        }
    }
}

现在的程序虽然能够体现"资源共享",程序出负票和同票,如何解决安全问题?

  • 校验多线程安全问题的标准是什么?

  • 1)查看当前程序是否是多线程环境 是

  • 2)是否存在共享数据 是 tickets

  • 3)是否有多条语句对共享数据进行操作 存在

    ​ 1),2)都必须具备:现在使用的就是多线程环境,而且存在共享数据

    从3)入手

    ​ Java提供一个同步机制:将多条语句对共享数据操作的使用同步代码块包括起来

    格式:

    ​ synchronized(锁对象){ //多个线程必须使用的同一把锁 (可以任意的Java类对 象)将多条语句对共享数据操作}

3.同步方法

//如果一个方法进来就是一个同步代码块---->
// 可以将synchronized抽取到方法声明上:同步方法 (非静态)
//同步方法:它的锁对象:this 当前类对象的地址值引用

public class SellTicket implements  Runnable {
    //100张票
    public static int tickets = 100 ;
//    public  Object obj = new Object() ;

    //定义一个统计变量
    int x = 0  ;

    @Override
    public void run() {
        //模拟一直有票
        while(true){
            if(x % 2 == 0){
//                synchronized (this){
                synchronized (SellTicket.class){
                    if(tickets>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                    }
                }
            }else{
                //x%2!=0
                sellTicket();
            }
            x ++ ;


        }
    }
    //如果一个方法进来就是一个同步代码块---->
    // 可以将synchronized抽取到方法声明上:同步方法 (非静态)
    //同步方法:它的锁对象:this 当前类对象的地址值引用
   /* private   synchronized  void sellTicket() {

            if (tickets > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
            }
    }*/

   静态的同步方法 ---静态的东西都和类直接相关 :锁对象---就是 (反射相关)类名.class--->class 包名.类名{}  字节码文件对象
    private  static  synchronized  void sellTicket() {

        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
        }
    }
}
4.Lock–手动锁

JDK5以后Java提供了比syncrhonized(jvm来操作:自动释放锁)更广泛的锁定操作,

程序员可以在某个位置自己去加锁,(弊端):手动释放锁
java.util.concurrent.locks.Lock 接口 不能实例化

提供成员方法

  • void lock() :获取锁

  • void unlock():手动试图释放锁

  • ReentrantLock :子实现类(可重入的互斥锁!) 跟synchronized用于法相似

  • synchronized: 关键字—> 内置语言实现—通过jvm调用的

    ​ 由jvm自动释放锁

    ​ 它不仅仅可以应用在方法体中(同步代码块),也可以引用在方法上(同步方法)

    Lock :是一个接口

    ​ 手动加锁和手动释放锁

    ​ 一般都是在方法中的语句中使用

public class SellTicket implements Runnable {

    //100张票
    public static int tickets = 100 ;

    //声明一个锁:Lock
    private Lock lock = new ReentrantLock() ;

    @Override
    public void run() {
        //模拟一直有票
        while(true){

            //获取一个锁:某个线程执行到这块:必须持有锁
            lock.lock();

            //执行完毕,手动释放锁
            //在开发中 :try...catch...finally :捕获异常 ,不会使用throws
            //变形格式:try...catch...catch...
            //try...finally...
            try{
                if(tickets >0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                }
            }finally {
                //释放资源代码块
                //释放锁
                lock.unlock();
            }

        }
    }
}
5.死锁

虽然syncrhonized可以解决线程安全问题:同步代码块/同步方法,但是执行效率低,可能出现死锁,两个线程或者多个线程出现互相等待的情况!

解决死锁问题方案:“生产者消费者思想” 必须保证多个多线程必须使用的同一个资源对象!

public class Student {

    String name ;
    int age ;

    //是否存在学生数据标记
    boolean flag ; //true有数据,false没有数据

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class SetThread implements  Runnable {

    private Student s ;
    public SetThread(Student s){
        this.s =  s ;
    }

    //统计变量
    int x = 0 ;
    @Override
    public void run() {

        //不断产生数据
        while(true){

            synchronized (s){
                //判断如果当生产者有数据
                if(s.flag){
                    try {
                        //生产者有数据,需要等待消费者线程使用
                        s.wait(); //锁对象在调用wait()
                        //wait():当前锁对象该调用方法的时候,会立即释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //当前在执行下面代码的之前:线程的执行资格被GetThread所在的消费者线程
                if(x % 2 == 0){
                    s.name  = "高圆圆" ;
                    s.age = 41 ;

                }else{
                    s.name = "杨德财" ;
                    s.age = 27 ;
                }
                x ++ ;
                //更改标记
                s.flag = true ;
                //通知对方线程:赶紧使用数据
                s.notify(); //锁对象调用的唤醒方法
            }
        }
       // Student s = new Student();
    }
}

public class GetThread implements Runnable {

    private Student s ;
    public GetThread(Student s){
        this.s = s ;
    }

    @Override
    public void run() {

        //模拟一直消费数据
        while(true){

            synchronized (s){
                if(!s.flag){
                    //如果消费者没有数据了,需要等待生产线程产生数据
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(s.name+"---"+s.age);

                //更改标记
                s.flag = false ;//没有数据了
                //通知对方线程:生产线程 :产生数据
                s.notify() ;

            }

        }


       // Student s = new Student() ;

    }
}

* 测试类
 *      SetThread:生产数据
 *      GetThread:消费数据
 *      Student:学生数据
 *
 *
 *      问题1 :出现数据 null--0 :
 *
 *      在生产资源类和消费者资源类中:分别new Student() :不是同一个对象
 *
 *
 *
 *      模拟一直有数据:加入循环操作while(true)
 *
 *      问题2:姓名和年龄不符
 *      线程的执行具有随机性导致,多线程程序不安全
 *
 *
 *      校验多线程安全问题的标准是什么?
 *          1)查看是否是多线程环境
 *          2)是否存在共享数据
 *          3)是否有多条语句对共享数据进行操作
 *
 *
 *          3)改进:使用同步代码块包括起来!
 *
 *
 *
 *          问题3:学生数据输出的时候,一次性打印很多
 *                  线程的一点点时间片:足够当前线程执行很多次!
 *
 *          优化:
 *          不需要输出学生数据的时候, 一次性输出很多,而是分别依次输出
 *                  "高圆圆" 41
 *                  "杨德财" 27
 *                  ...
 *                  ..
 *
 */
public class ThreadDemo {

    public static void main(String[] args) {

        Student s = new Student() ; //同一个资源类对象
        //创建生产资源类对象
        SetThread st = new SetThread(s) ;
        //创建消费者资源类对象
        GetThread gt = new GetThread(s) ;

//        创建线程类对象
        Thread sThread = new Thread(st) ;
        Thread gThread = new Thread(gt) ;

        //启动
        sThread.start();
        gThread.start();

    }
}

6.线程组

ThreadGroup 线程组表示一个线程的集合

Thread类中的方法:

​ Public final ThreadGroup getThreadGroup():获取当前所有的默认线程组

​ ThreadGroup

​ public final String getName():获取默认线程组的名称(默认就是main)

构造方法:

ThreadGroup(String name):构造一个新的名称的线程组

​ 所有的线程默认的线程组就是main线程(用户线程)

4.线程池
1.线程池的使用:

​ (1)接口:ExecutorService-----跟踪一个或者多个异步任务

​ (2)如何实例化----->Executors.newFixedThreadPool(int)工厂方法:

​ Executors:工厂类

​ 提供创建线程池对象的方法

​ public static ExecutorService newFixedThreadPool(int nThreads)

​ ExecutorsService

​ 方法:

​ Future<?> submit(Runnable task) :通过线程池提交异步任务

​ Future submit(Callable task):提交异步任务

​ Future:异步任务计算的结果!

​ 第三种方式:线程池实现---->还是自定义一个类 实现Runnable接口

public class ThreadDemo {
    public static void main(String[] args) {
       /* new Thread(){
            @Override
            public void run() {
            }
        }.start();*/
        //创建一个线程池:静态工厂模式
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //提交异步任务
        // Future<?> submit(Runnable task) :通过线程池提交异步任务
       /* pool.submit(new MyRunnable()) ;
        pool.submit(new MyRunnable()) ;*/
       //Callable要比Runnable接口更好
        //可以跟踪具体的异常错误(如果执行过程中,线程出现异常,可以跟踪异常信息!)
       pool.submit(new MyCallable()) ;
       pool.submit(new MyCallable()) ;

        //使用完毕,关闭线程池--将底层产生的线程归还线程池中
        pool.shutdown();
       }
        }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mSGfpN0D-1622122360804)(D:\JAVA 笔记\05-27\resource\02_线程池的引入.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8EjlwAyn-1622122360805)(D:\JAVA 笔记\05-27\resource\03_线程池的七大参数.png)]
2.定时器

java.util.Timer:

定时器 :有线程安排执行务执行一次,或者定期重复执行。

构造方法:

​ public Timer() 无参构造方法

成员方法:

​ public void cancel():取消定时器

​ public void schedule(TimerTask task,Date time) :在指定日期时间内执行这 个任务

​ public void schedule(TimerTask task,long delay):在指定的延迟时间后执行 task任务(时间:毫秒)

​ public void schedule(TimerTask task,long delay,long period) :在指定的 delay延迟时间后开始重复时间间隔period来执行task任务

  • Java设计原则:
  •  单一职责
    
  •  接口分离
    
  •  开闭原则
    
  •  依赖注入原则(依赖于抽象类或者接口,而不依赖具体类实现)
    
public class TimerDemo {
    public static void main(String[] args) {
        //创建一个定时器
        Timer timer  = new Timer() ;
        //   public void cancel():取消定时器

        //参数1:定时任务:抽象类,定义子类继承自TimerTask
       // timer.schedule(new MyTask(timer),3000); :3秒后执行一次这个任务

      /*  public void schedule(TimerTask task,
                     long delay,
                       long period) :在指定的delay延迟时间后开始重复时间间隔period来执行task任务		*/

      timer.schedule(new MyTask(timer),2000,3000);
    	}
		}

//定时任务
class MyTask extends TimerTask{ //TimerTask实现Runnable接口         ---会使用同步机制
    private Timer t ;
    public MyTask(Timer t){
        this.t = t ;
    }

    @Override
    public void run() {

        System.out.println("bom...");
        //关闭定时器
        //t.cancel() ;

    }
}
5.代理

代理:

​ 让别人替自己本身完成一些事情!

​ 举例: 过年回家买车票,让别人帮忙代买!

代理:

​ 代理角色:帮助真实角色对他本身的功能进行增强(完成代理完成不了的事情!)

​ 真实角色:只只专注于自己完成的事情

​ 举例: 结婚这件事情

​ 真实角色:you :你

​ 代理角色:weddingCompany:婚庆公司

​ Java代理模式----结构型设计模式

静态代理

​ 同一个接口

​ 特点:代理角色和真实角色必须同一个接口

动态代理(后期框架底层使用的这个模式)

​ JDK动态代理:基于接口

​ CGLib动态代理:基于子类

  •                多线程的实现方式2:
    
  •                MyRunnable myrunnable = new MyRunnable() ;  //MyRunnable implements Runnable接口
    
  •                //线程类
    
  •                Thread t1 = new Thread(myrunnable,"t1") ;   //Thread类本身实现 Runnable接口
    
  •                Thread t2 = new Thread(myrunnable,"t2") ;
    
  •                Thread t3 = new Thread(myrunnable,"t3") ;
    
public class StaticProxyDemo {

    public static void main(String[] args) {

        //接口多态
        //真实角色
        Marry marry = new You() ;
        marry.marry() ;

        System.out.println("-----------------------");

        //加入婚庆代理角色
        You you = new You() ;
        //创建婚庆公司
        WeddingCompany wdc = new WeddingCompany(you) ;
        wdc.marry();
    }
}

//定义接口:Marry
interface  Marry{
    public abstract  void marry() ;
}

//定义一个子实现类:真实角色
class You implements Marry{

    @Override
    public void marry() {
        System.out.println("我很高兴,我要结婚了...");
    }
}

//代理角色weddingCompany:婚庆公司 要实现Marry接口
class WeddingCompany implements  Marry{
    private You you ;
    public WeddingCompany(You you){
        this.you = you ;
    }

    @Override
    public void marry() {
        System.out.println("结婚之前,婚庆公司布置婚礼现场...");
        you.marry();
        System.out.println("结婚完毕,很高兴,给婚庆付尾款...");
    }
}

System.out.println("-----------------------");

    //加入婚庆代理角色
    You you = new You() ;
    //创建婚庆公司
    WeddingCompany wdc = new WeddingCompany(you) ;
    wdc.marry();
}

}

//定义接口:Marry
interface Marry{
public abstract void marry() ;
}

//定义一个子实现类:真实角色
class You implements Marry{

@Override
public void marry() {
    System.out.println("我很高兴,我要结婚了...");
}

}

//代理角色weddingCompany:婚庆公司 要实现Marry接口
class WeddingCompany implements Marry{
private You you ;
public WeddingCompany(You you){
this.you = you ;
}

@Override
public void marry() {
    System.out.println("结婚之前,婚庆公司布置婚礼现场...");
    you.marry();
    System.out.println("结婚完毕,很高兴,给婚庆付尾款...");
}

}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-01gVyEjj-1622122360807)(D:\JAVA 笔记\05-27\resource\01_代理真实用途.png)]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值