java回顾——多线程

一,问题:

  • 什么是线程,为什么要学习它?
  • java多线程我们要学习什么?

二,线程(操作系统中的概念):

参考进程:https://blog.csdn.net/weixin_44841312/article/details/104966035

参考线程:https://blog.csdn.net/weixin_44841312/article/details/105191973

学习线程之前要知道:

  • 并发与并行关系:并发指的是一段时间内多个程勋交替运行,并行指一段时间内多个程勋同时运行。
  • 进程:是一个程序执行的过程,是系统运行程序的基本单位。(但你启动QQ程序,本质上内存中就有了QQ这个进程)
  • 线程:线程是进程中的小进程,比如QQ程序中的聊天功能就是一个线程,QQ空间又是另一个线程
  • 为什么有多线程:为了提高进程效率(程序运行的速度)
  • 线程的调度:分时调度(轮流公平调度),抢占式调度(优先级高的可以抢CPU进行调度)

三,java多线程要学习什么?

  • 常用创建线程方法
  • 线程安全问题
  • 线程之间状态转变
  • 线程同步的代码实现
  • 线程池

四,开始学习:

1,常用创建线程方法:

1,继承Thread类并重写它的run方法。(实现步骤在代码中)

  • 下面people继承了Thread类,重写了run方法(该线程要执行的任务),使用people的对象p1和p1调用start方法启动两个线程。start方法会调用run方法
  • start方法会在jvm中开辟新的栈空间,对其中的run方法压栈执行
/**
 * 实现步骤:
 * 1.创建一个Thread类的子类I
 * 2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么?)
 * 3.创建Thread类的子类对象
 * 4.调用Thread类中的方法start方法,开启新的线程,执行run方法
 * void start() 使该线程开始执行; Java虚拟机调用该线程的run 方法。
        * 结果是两个线程并发地运行;当前线程(main线程)和另-一个线程(创建的新线程,执行其run方法)。
        * 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
        * java程序属于抢占式调度,那个线程的优先级高,那个线程优先执行;同-一个优先级,随机选择一个执行
 */
public class RunTest {
    public static void main(String[] args) throws InterruptedException {//主线程
        People p1=new People("建江");
        People p2=new People("小明");
        p1.setName("线程1");//设置线程名称
        p1.start();//线程1
        p2.start();//线程2
        for(int i=0;i<10;i++){
            System.out.println("main"+i);
            Thread.sleep(1000);//线程睡眠1秒
        }


    }
}
public class People extends Thread {
    private String name;
    public People(){

    }
    public People(String name){
        this.name=name;
    }


    @SneakyThrows
    public void run(){
        for (int i=0;i<10;i++){
            System.out.println(name+i);
            //String name = getName();//获取线程的名称
            String name = Thread.currentThread().getName();
            System.out.println("线程名称:"+name);
            Thread.sleep(1000);//使得线程睡眠
        }
    }
}

2,实现实现Runnable接口,实现run方法(实现步骤在代码中)

/**
 * 实现步骤:
 * 1.创建一个Runnable接口的实现类Dog
 * 2.在实现类中重写Runnable接口的run方法,设置线程任务
 * 3.创建一个Runnable接口的实现类对象
 * 4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象
 * 5.调用Thread类中的start方法,开启新的线程执行run方法
 */
public class RunTest {
    public static void main(String[] args) throws InterruptedException {//主线程
        Dog dog1=new Dog("金毛");
        Dog dog2=new Dog("泰迪");
        Thread thread1=new Thread(dog1);
        Thread thread2=new Thread(dog2);
        thread1.start();
        thread2.start();
    }
}
public class Dog implements Runnable {
    String name;
    public Dog(){}
    public Dog(String name){
        this.name=name;
    }
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(name+i);
        }
    }
}

 

3,这两种创建线程的方法有什么区别(使用Runable接口实现多线程的好处)

  • 避免了单继承的局限性:
  • 类继承了Thread类就不能继承其他的类
  • 实现了Runnable接口,还可以继承其他的类,实现其他的接口
  • 增强了程序的扩展性,降低了程序的耦合性(解耦):
  • 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
  • 实现类中,重写了run方法;用来设置线程任务
  • 创建Thread类对象,调用start方法:用来开启新线程

4,匿名内部类的方式创建线程

public class RunTest {
    public static void main(String[] args){//主线程
       new Thread(){
           @Override
           public void run(){
               for (int i=0;i<10;i++){
                   this.setName("建江");
                   System.out.println(Thread.currentThread().getName()+i);
               }
           }
       }.start();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        };
        new Thread(runnable).start();

        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

 

2,同步安全问题:

  • 什么是同步安全问题:
  • 如何解决这个问题:同步代码块,同步方法,锁机制
  • 买票的例子(多个线程访问会出现卖重复的票,-1的票)

调用的测试方法:

public class MovieTest {
    public static void main(String[] args) {
        Movie movie=new Movie();
        Thread t1=new Thread(movie);
        Thread t2=new Thread(movie);
        Thread t3=new Thread(movie);
        t1.start();
        t2.start();
        t3.start();


    }
}

使用同步代码块解决:

public class Movie implements Runnable {
    private Integer ticket=100;
    Object lock=new Object();//创建一个对象锁
    @Override
    public void run() {
        /**
         * lock对象只有一个,那个线程得到了lock对象,就可以执行代码块中的内容
         * 保证了对临界资源的互斥访问
         */
        
        while (true){
            synchronized (lock){//给会出现安全问题的代码加锁
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (ticket>0){
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                    ticket--;
                }
            }
        }

    }}

使用同步方法解决:

public class Movie implements Runnable {
    private Integer ticket=100;
    Object lock=new Object();//创建一个对象锁
    @Override
    public void run() {
        /**
         * lock对象只有一个,那个线程得到了lock对象,就可以执行代码块中的内容
         * 保证了对临界资源的互斥访问
         */

        while (true){
            sellTicket();
        }

    }

    public synchronized void sellTicket(){//所对象是this
        if (ticket>0){
            System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
            ticket--;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

使用Lock机制解决:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Movie implements Runnable {
    private Integer ticket=100;
    Lock MyLock=new ReentrantLock();//创建一个获取锁的对象
    @Override
    public void run() {
        while (true){
            MyLock.lock();//上锁
            if (ticket>0){
                try {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    MyLock.unlock();//释放锁
                }

                }



        }

    }
}

3,线程间状态的改变:

线程间的状态改变结构如下

消费者,与生产者进程交互调用wait方法和notify方法的案例:(包子铺卖包子)

public class CommuteTest {
    private static int i=1;
    public static void main(String[] args) {

       Object o=new Object();
        new Thread(){
            @Override
            public void run(){
                while (true){
                    synchronized (o){
                        System.out.println("告诉老板包子的种类和数量,等待5秒");
                        try {
                            o.wait();//等待老板做包子
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("顾客"+i+"得到包子,开始吃包子");
                        System.out.println("顾客"+i+"吃完包子跑了了===========================================");
                        i++;
                    }
                }


            }
        }.start();
        new Thread(){
            @Override
            public void run(){
                while (true){
                    try {
                        Thread.sleep(5000);//花费5秒做包子
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o){
                        System.out.println("老板做好包子了");
                        o.notify();
                    }
                }

            }
        }.start();


    }
}

4,线程池?

  • 什么是线程池:它是一个存储线程的集合,其中的线程可以借出来使用,使用结束后归还到池子。
  • 线程池的优点:线程池中的线程可以被反复使用,减少了资源的消耗,提高了响应速度
  • JDK1.5之后有内置线程池。
  • 线程池的使用:

线程池:JDK1.5之后提供的

  • Executors类 :线程池的工厂类,用来生成线程池
  • Executors类中的静态方法:
  • newFxedThreadPool(int nThreods) 创建一个可重用固定线程数的线程池,传入参数是线程个数,返回的是值是ExecutorService接口的实现类对象
  • ExecutorService:线程池接口:用来从线程池中获取线程,调用start方法,执行线程任务,submit(Runnable task) 提交一个Runnable任务用于执行

线程池的使用步骤:

  1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
  2. 创建一个类, 实现Runnable接口,重写run方法,设置线程任务
  3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
  4. 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
  5. 具体实现:
public class Dog implements Runnable {
    String name;
    public Dog(){}
    public Dog(String name){
        this.name=name;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"执行了线程");

    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PoolTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Dog dog1=new Dog("金毛");
        Dog dog2=new Dog("泰迪");
        executorService.submit(dog1);
        executorService.submit(dog2);
        executorService.submit(new Dog());
        executorService.submit(new Dog());
        executorService.submit(new Dog());

    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值