Java回顾-多线程

一、线程创建

方式一、继承Thread类

        1.定义新类继承Thread类

        2.重写run()方法

        3.创建新类对象,调用 .start()方法

//方式1.1 继承Thread类
class thread1 extends Thread{
    @Override
    public void run() {
        System.out.println("线程1的操作...");
    }
}


    @Test
    public void test1(){
        thread1 t1=new thread1();
        t1.start();
    }


//方式1.2 继承Thread类,但用匿名内部类实现
    @Test
    public void test2(){
        //1.2 采用匿名子类的方式
        new Thread(){
            @Override
            public void run() {
                System.out.println("线程1的操作...");
            }
        }.start();
    }

方式二、实现Runable接口

        1.定义新类实现Runable接口

        2.重写run()方法

        3.创建Thread类的实例构造器参数为新类

        4.Thread类的实例调用 .start()方法

        源码解读:Thread类的构造器有个Runnable类型参数target,调用Threadrun()时实际上执行的是targetrun()target就是我们自定义的类里面的run()

class thread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("线程2的操作...");
    }
}


 @Test
    public void test3(){
        Thread t2=new Thread(new thread2());
        t2.start();
    }

两种方式的比较:

    -开发中一般用第二种方式

    原因:1.实现的方式没有类的单继承性的局限性

              2.实现的方式更适合来处理多个线程有共享数据的情况

    联系:Thread 类也继承于 Runnable( )接口

    相同点:两种方式都需要重写run(),将线程要执行的操作声明在run()中

方式三、实现 Callable 接口

          与Runnable相比,Callable功能更强大:

                -相比run()可以有返回值

                -方法可以抛出异常

                -支持泛型的返回值

                -需要借助FutureTask类,比如获取返回结果

  Future接口

                -可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。

                -FutureTask是Future接口的唯一实现类,同时实现了Runnable、Future接口

实现步骤:

        1.创建 Callable实现类

        2.实现 call方法,将此线程需要执行的操作声明在call()

        3.创建Callable 接口实现类的对象

        4.将Callable 接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象

        5.将FutureTask的对象作为参数传递到Thread类构造器中,创建Thread对象,并调用start()

        6.(可选)获取Callable中call方法的返回值  - .get

class Thread3 implements Callable{
    @Override
    public Object call() throws Exception {
        System.out.println("线程3的操作");
        return 521;//可以有返回值
    }
}


public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread3 thread3=new Thread3();//新建线程的对象
        FutureTask futureTask=new FutureTask(thread3);//FutureTask 对象
        new Thread(futureTask).start();// Thread 对象.start()
        futureTask.get();// 获取该线程的返回值
    }

方式四、使用线程池

        思路:提前创建好多个线程,放入线程池中,使用时直接获取,是用完放回池中

        好处:提高响应速度、降低资源消耗、便于线程管理

        相关API:

        ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

                -void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行                 Runnable

                -Future submit(Callable task):执行任务,有返回值,一般又来执行 Callable

                -void shutdown() :关闭连接池

         Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池                                  Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池                              Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池                                  Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池                                  Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。


class Thread4 implements Runnable{
    @Override
    public void run() {
        System.out.println("线程4");
    }
}
class Thread5 implements Callable{
    @Override
    public Object call() throws Exception {
        System.out.println("线程5");
        return 521;
    }
}



public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service= Executors.newFixedThreadPool(10);
        service.execute(new Thread4());//执行实现Runnable接口的线程
        service.submit(new Thread5());//执行实现Callable接口的线程
        service.shutdown();//关闭所有线程
    }
}


线程4
线程5

 二、线程类的相关方法

    1. start() :启动当前线程:调用当前线程的run()

    2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中

    3. currentThread():静态方法,返回执行当前代码的线程

    4. getName():获取当前线程的名字

    5. setName():设置当前线程的名字     可以在重写的Thread类的构造器中重命名

    6. yield():释放当前cpu的执行权 

    7. join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到线程b完全执行完,a才结束阻塞状态

    8. sleep():让当前线程睡眠指定毫秒的时间,使得当前线程进入阻塞状态,在Thread中声明

,不会释放锁(同步监视器)

三、线程的优先级

            MAX_PRIORITY:10

            NORM_PRIORITY:5  (默认值)

            MIN_PRIORITY:1

      高优先级的线程会抢占低优先级线程的cpu执行权,但这只是从概念上讲的,不是高优先级执行完才执行低优先级。

       涉及的方法

        getPriority() :返回线程优先值

        setPriority(int newPriority) :改变线程的优先级

四、线程的生命周期

 

    -每个线程用有独立的运行程序计数器pc

    -一个进程中的多个线程共享相同的内存单元,他们从同一个中分配对象,可以访问相同的变量和对象

    -java应用程序至少有三个线程:main() gc() 垃圾回收线程、异常处理线程

五、线程的同步

1.线程安全问题

         下面是火车售票的例子,3个线程再判断是否有余票的时候都阻塞了,则恢复执行后数据就出错了:

解决方法:一般通过同步机制来解决线程的安全问题

        -方式一、同步代码块                        

Synchronized(this/newthread.class){

          //需要被同步的代码块

        }

        说明:1.操作共享数据(多个线程共同操作的变量)的代码即为需要被同步的代码

                  2.同步监视器:俗称,可以是任一个类的对象

        要求:多个线程必须共用一把锁

                  3.在实现Runnable接口创建多线程的方式中,可以使用 this 充当同步监视器,但继承Thread方式时不要用 this 作为锁,因为 this 会分别指代多个线程的对象,可以用 类.class 代替 this

        

   -方式二、同步方法

                    -把需要同步的代码块拿出来单独作为一个方法,声明时加上synchronized.                

private synchronized void test(){

          // 需要被同步的代码块

        }

      总结

1、同步方式的不足之处:操作同步代码时只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低

2、同步方法仍然涉及到同步监视器,只是不需要显式的声明

3、非静态的同步方法,同步监视器是this

     静态的同步方法,同步监视器是当前class本身

2.线程的死锁问题 

        死锁 :不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

        出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于 阻塞状态,无法继续

        解决方法 :

                专门的算法、原则

                尽量减少同步资源的定义

                尽量避免嵌套同步


     所以引入了方式三:

        -方式三、Lock锁 (-jdk5.0+)

private final ReentrantLock lock = new ReenTrantLock();
try{
    lock.lock();
    //有隐患的代码块
}finally{
    lock.unlock();
}

        Lock与Synchronized 区别

        1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是 隐式锁,出了作用域自动释放

        2. Lock只有代码块锁,synchronized有代码块锁方法锁

        3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

六、线程的通信

1.常用方法

        wait()一旦执行,当前线程就进入阻塞状态,并释放同步监视器

        notify()一旦执行,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的

        notifyAll()一旦执行,就会唤醒被wait的所有线程

        说明:

                1.wait()、notify()、notifyAll()三个方法必须使用在同步代码块同步方法中(synchronized)

                2.三者的调用者必须是同步代码块同步方法中的同步监视器,否则出现异常

                3.三者定义在object类

        wait()与 sleep()的对比

                1.sleep()再Thread类中声明,wait()在object类中声明。

                2.sleep()可在任何场景下调用,wait()只能在同步代码块同步方法中用

                3.sleep()不释放同步锁,wait()会释放。

2.举例-生产者消费者模式

        生产者负责生产产品,消费者负责消费产品,店员负责转交产品。店员取货和送货的速度都是随机的,全程不间断进行。

public class test0 {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();
        Thread p1=new Thread(new Producer(clerk));
        Thread c1=new Thread(new Customer(clerk));
        p1.setName("1.生产者");
        c1.setName("2.消费者");
        p1.start();
        c1.start();
    }
}
class  Clerk{
    private int productCount=0;
    Random random=new Random();
    public synchronized void produceProduct() throws InterruptedException {
            if(productCount<20){
                productCount++;
                System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
                Thread.sleep(random.nextInt(250));
                notify();
            }else {
                wait();
            }
    }
    public synchronized void consumeProduct() throws InterruptedException {
            if(productCount>0){
                 System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
                 productCount--;
                 Thread.sleep(random.nextInt(250));
                 notify();
            }else {
                wait();
            }
    }
}
class Producer implements Runnable{
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println("开始生产产品...");
        while (true){
            try {
                clerk.produceProduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Customer implements Runnable{
    private Clerk clerk;

    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println("开始消费产品...");
        while (true){
            try {
                clerk.consumeProduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值