Java多线程总结

线程创建三种方式

​ 1、继承Thread类 线程不一定立即执行,CPU安排调度

​ 2、实现Runnable接口 把Runnable接口的实现类丢进Thread里面,再调用start(代理)

​ 3、实现Callable接口(了解) 1、创建执行服务 2、提交执行 3、获取结果 4、关闭服务

​ Callable 实现多线程好处:
​ 1、可以定义返回值
​ 2、可以抛出异常

/**
 * 回顾总结线程的创建
 */

public class ThreadNew {

    public static void main(String[] args) {
        new MyThread1().start();
        new Thread(new MyThread2()).start();


        FutureTask<Integer> futureTask=new FutureTask<Integer>(new MyThread3());
        new Thread(futureTask).start();
        try {
            Integer integer = futureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

}

//1、继承Thread类
class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread1");
    }
}

//2、实现Runnable接口
class MyThread2 implements Runnable{

    @Override
    public void run() {
        System.out.println("MyThread2");
    }
}

//实现Callable接口
class MyThread3 implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 100;
    }
}


run()方法线程执行体,start()方法启动线程

继承Thread类 启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性

实现Runnable接口 启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
(类的单继承和接口的多继承)

静态代理的思想。
Thread代理真实的Runnable接口的实现。

/**
 * 线程创建的方式1:继承Thread类 重写run()方法,调用start开启线程
 * 注意:线程开启不一定立即执行,由cpu调度执行。
 */

public class CreatThread1 extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我是run线程"+i);
        }
    }

    public static void main(String[] args) {

        //创建一个线程对象
        CreatThread1 creatThread1=new CreatThread1();

        //调用start()方法开启线程
        creatThread1.start();


        for (int i = 0; i < 20; i++) {
            System.out.println("我是主线程"+i);
        }
    }
}
/**
 * @author 超厉害的我啊
 * @date 2021/3/11 14:36:42
 *
 * 创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢人runnable接口实现类,调用start方法
 */

public class CreatThread2 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我是run线程"+i);
        }
    }

    public static void main(String[] args) {

        //创建runnable接口的实现类对象
        CreatThread2 creatThread2=new CreatThread2();
        //创建线程对象,通过线程对象来开启我们的线程,代理
//        Thread thread=new Thread(creatThread2);
//        thread.start();
        new Thread(creatThread2).start();


        for (int i = 0; i < 20; i++) {
            System.out.println("我是主线程"+i);
        }
    }

/**
 * Callable 实现多线程好处
 * 1、可以定义返回值
 * 2、可以抛出异常
 */

public class TestCallable implements Callable<Boolean> {


    //网络图片地址
    private String url;
    //保存的文件名
    private String name;

    public TestCallable(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public Boolean call() {
        WebDownloader2 webDownloader=new WebDownloader2();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1=new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=1.jpg","0.jpg");
        TestCallable t2=new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=2.jpg","1.jpg");
        TestCallable t3=new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=3.jpg","2.jpg");

        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> r1=ser.submit(t1);
        Future<Boolean> r2=ser.submit(t2);
        Future<Boolean> r3=ser.submit(t3);

        //获取结果
        boolean rs1=r1.get();
        boolean rs2=r2.get();
        boolean rs3=r3.get();

        //关闭服务
        ser.shutdown();

    }

}

class WebDownloader2{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

public class TestThread2 implements Runnable{

    //网络图片地址
    private String url;
    //保存的文件名
    private String name;

    public TestThread2(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public void run() {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        TestThread2 t1=new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=0.jpg","0.jpg");
        TestThread2 t2=new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=0.jpg","1.jpg");
        TestThread2 t3=new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=0.jpg","2.jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();

    }
}


class WebDownloader{
    //下载方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

线程的五大状态:

​ 1、创建状态 new出来的时候
​ 2、就绪状态 调用start方法
​ 3、阻塞状态 sleep、wait、同步锁定
​ 4、运行状态
​ 5、死亡状态 一旦进入死亡状态,线程就不能再次启动

线程停止:

1、建议线程正常停止---->利用次数,不建议死循环
2、建议使用标志位------>设置一个标志位
3、不要使用stop或者destroy等过时或者JDK不建议使用的方法

/**
* 测试stop
* 1、建议线程正常停止---->利用次数,不建议死循环
* 2、建议使用标志位------>设置一个标志位
* 3、不要使用stop或者destroy等过时或者JDK不建议使用的方法
*
*/

public class TestStop implements Runnable{

   //设置一个标识位
   private boolean flag=true;


   @Override
   public void run() {
       int i=0;
       while (flag){
           System.out.println("run----"+i++);
       }
   }

   //设置一个公开的方法停止线程,转换标志位
   public void stop(){
       this.flag=false;
   }


   public static void main(String[] args) {
       TestStop testStop=new TestStop();
       new Thread(testStop).start();

       for (int i = 0; i < 1000; i++) {
           System.out.println("main"+i);
           if(i==900){
               //调用stop方法切换标志位,让线程停止
               testStop.stop();
               System.out.println("线程该停止了");
           }
       }
   }

}

线程休眠:

​ sleep(时间)指定当前线程阻塞的毫秒数
​ sleep存在异常InterruptedException
​ sleep时间达到后 线程进入就绪状态
​ sleep可以模拟网络延迟、倒计时等
​ 每个对象都有一个锁,sleep不会释放锁

/**
 * 模拟倒计时
 */

public class TestSleep2 {


    public static void main(String[] args) {
        //打印当前系统时间
        Date startTime=new Date(System.currentTimeMillis());//获取系统当前时间
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime=new Date(System.currentTimeMillis());//更新当前时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    //模拟倒计时
    public static void tenDown() throws InterruptedException {
        int num=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num<=0){
                break;
            }
        }
    }

}

线程礼让(Yield)

​ 让当前正在执行的线程停止,但不阻塞
​ 将线程从运行状态转为就绪状态
​ 让cpu重新调度,礼让不一定成功,看cpu心情

**
 * 测试礼让线程
 * 礼让不一定成功,看cpu心情
 */

public class TestYield {

    public static void main(String[] args) {
        MyYield myYield=new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }


}

class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

线程的优先级Priority

​ Java提供一个线程调度器来监控启动后进入就绪状态的所有线程
​ 线程的优先级用数字表示,范围1-10 数字大的优先级高,优先级高只是意味着获得调度的概率低,真正被调用还是靠Cpu调度

守护(daemon)线程

​ 线程分为用户线程(main)和守护线程(gc)
​ 虚拟机必须确保用户线程执行完毕
​ 虚拟机不用等待守护线程执行完毕
​ 如后台记录操作日志、监控内存、垃圾回收等待

/**
 * 测试守护线程
 * 神仙保佑你
 */

public class TestDaemon {

}


//神仙
class God implements Runnable{

    public static void main(String[] args) {
        God god=new God();
        You you=new You();

        Thread thread=new Thread(god);
        thread.setDaemon(true);//默认是false。表示是用户线程,正常的线程都是用户线程
        //守护线程启动
        thread.start();
        new Thread(you).start();
    }


    @Override
    public void run() {
        while (true){
            System.out.println("神仙保佑者你");
        }
    }
}

//你
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("每天都开心");
        }
        System.out.println("一直很开心");
    }
}

线程不安全的三种例子:

​ 车站买票
​ 银行取钱
​ 线程不安全的集合

线程同步:

​ 同步方法:
​ synchronized方法,默认锁的是this(对象本身)
​ 缺点:如果将一个大的方法声明为synchronized将会影响效率
​ synchronized块
​ synchronizen(Obj){}Obj的对象是变化的量,需要增删改的对象

死锁:(多个线程互相抱着对方需要的资源,然后形成僵持)

​ 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能
​ 运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情
​ 形,某一个同步块同时拥有两个以上对象的锁时,就可能发生死锁的问题

产生死锁的四个必要条件:

​ 1、互斥条件:一个资源每次只能被一个进程使用
​ 2、请求与保持条件:一个进程因请求而阻塞时,对已获得的资源保持不放
​ 3、不可剥夺条件:进程已获得的资源,在未使用完之前,不难强行剥夺
​ 4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
​ 避免死锁的方法就是破坏死锁的四个必要条件之一

Lock(锁)

​ JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。
​ java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具
​ ReentrantLock(可重入锁)类实现了Lock

/**
 * 测试Lock锁
 */

public class TestLock {

    public static void main(String[] args) {
        TestTicket2 testTicket2=new TestTicket2();

        new Thread(testTicket2).start();
        new Thread(testTicket2).start();
        new Thread(testTicket2).start();
    }

}

class TestTicket2 implements Runnable{

    int ticketNums = 10;

    //定义lock锁
    private final ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
            lock.lock();//加锁
                if(ticketNums>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            } finally {
                //解锁
                lock.unlock();
            }

        }
    }
}

Synchronized与Lock的对比

​ 1、Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronizen是隐式锁,出了作用域自动释放
​ 2、Lock只有代码块锁,synchronized有代码块锁和方法锁 3、使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)
​ 4、优先使用顺序
​ Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)

线程通信

​ 方法:作用
​ wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
​ wait(long timeout):指定等待的毫秒数
​ notify():唤醒一个处于等待状态的线程 notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

线程池

​ JDK5提供线程池相关API:ExecutorService和Executors
​ ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor
​ void execute(Runnable) 执行任务/命令,没有返回值 一般用来执行Runnable
​ Futuresubmit 执行任务,有返回值,一般用来执行Callable
​ void shutdown() 关闭连接池
​ Executors 工具类,线程池的工厂类,用于创建并返回不同类型的线程池

/**
 * 测试线程池
 *
 */

public class TestPool {

    public static void main(String[] args) {
        //1、创建服务,创建线程池
        //newFixedThreadPool 参数为:线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);

        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //关闭连接
        service.shutdown();


    }

}

class MyThread implements Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值