大数据 -- java14 多线程 线程池 同步安全机制

1、Thread : 线程
     创建多线程的第一种方式: 继承Thread类。      
     创建多线程的第二种方式: 实现Runnable接口。   
     创建多线程的第三种方式: 实现Callable接口。  

    第一种方法步骤:
        (1)创建一个自定义类继承Thread类。
        (2)这个类要重写Thread类中的run方法。
             为什么是run()方法呢?
             当线程启动之后,执行的代码逻辑仅是run()方法的代码逻辑。
        (3)根据这个类创建线程对象。
        (4)启动线程。 star()方法。
public class MyThread1 extends Thread {

    @Override
    public void run() {
        //super.run();

        //写我们要线程执行的逻辑代码。
        //一般来说,被线程执行的逻辑代码都是比较耗时的,为了模拟这里的耗时,使用循环打印。
        for (int i = 1; i < 200; i++) {
            System.out.println(i);
        }
    }
}



public class MyThread1Demo {
    public static void main(String[] args) {

        //1、每创建一个对象,相当于创建了一个新的线程对象
//        MyThread myThread = new MyThread();

        //2、启动线程
//        myThread.run();    //单纯的调用run()方法仅仅表示的是一个对象调用普通的方法,所以这里依旧是单线程程序
//        myThread.run();

        //3、要想看到多线程的效果,就必须换一种方式启动线程 : start()
//        myThread.start();

        //4、当一个线程对象启动多次的时候,   报错: IllegalThreadStateException  非法的线程状态异常
//        myThread.start();

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


    //正确的创建线程的方法:
        //1、至少创建2个及两个以上的线程对象
        MyThread1 myThread1 = new MyThread1();
        MyThread1 myThread2 = new MyThread1();
        MyThread1 myThread3 = new MyThread1();

        //2、启动线程
        myThread1.start();
        myThread2.start();
        myThread3.start();

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


    //匿名内部类创建线程对象
        Thread t1 = new Thread("昭阳") {
            @Override
            public void run() {
                for (int i = 0; i < 200; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        };

        t1.start();
    }
}
    第二种方法步骤:
        (1)自定义一个类实现Runnable接口。
        (2)实现run()方法。
        (3)创建自定义类对象。
        (4)创建Thread线程对象,将自定义的对象作为参数传递到构造方法中。
public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 200; i++) {

            //由于Runnable接口中没有getName()方法,所以这里无法使用获取线程对象名字。
            //间接调用: 我们可以先获取当前线程的对象,然后再调用Thread类中getName()方法。
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}



public class MyRunnableDemo {
    public static void main(String[] args) {

        //1、创建自定义类对象(这里不是线程对象!!!)
        MyRunnable myRunnable = new MyRunnable();

        //2、创建2个线程对象
        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);
        Thread t3 = new Thread(myRunnable,"昭阳1");

        //3、给线程起名字
        t1.setName("昭阳");
        t2.setName("米彩");

        //4、启动线程
        t1.start();
        t2.start();
        t3.start();

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



        //匿名内部类创建线程对象
        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 200; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        },"昭阳");

        t4.start();
    }
}
    第三种方法步骤:
        (1)自定义类实现Callable接口。
        (2)实现call方法。
        (3)该线程的启动必须与线程池结合,单独无法创建线程对象启动。
public class MyCallable implements Callable {

    @Override
    public Object call() throws Exception {     // 需要返回一个Object类型的值

        Object o = new Object();      // 这里随便new一个Object的值就行了

        for (int i = 1; i <= 200; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }

        return o;
    }
}



public class ThreadPoolDemo2 {
    public static void main(String[] args) {

        //1、创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);      // 表示线程池中最多允许2个线程在执行任务。

        //2、创建自定义Callable对象
        MyCallable myCallable = new MyCallable();     // 这不是一个线程对象!!!

        //3、提交到线程池中执行
        pool.submit(myCallable);     //当myCallable作为参数传入时,被底层封装成了一个线程对象并启动执行。    结果: pool-1-thread-1:1 ... 1:200

        //4、手动停止线程池
        pool.shutdown();

        //线程池已经被关闭了,不能再继续提交任务。若再提交,会报错。
    }
}

2、线程池:

   程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。

   而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
   线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

   在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池


创建线程池步骤:
   (1)创建线程池对象:
       Executors工厂类 下有很多获取线程池的静态方法, newFixedThreadPool是其中的一种线程池:
               public static ExecutorService newFixedThreadPool(int nThreads)

   (2)在线程池中存放线程:(可以存放哪些线程呢?  Runnable、Callable)
       Future<?> submit(Runnable task)   提交一个可运行的任务执行,并返回一个表示该任务的未来。

   (3)在线程池中的线程如何去运行呢?
        线程池对象.submit() 提交即运行。

   (4)想要结束线程的运行,怎么手动结束?
        void shutdown()   启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。

        线程池关闭后就不可以再提交运行了。否则报错: RejectedExecutionException
public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 200; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}



public class ThreadPoolDemo {
    public static void main(String[] args) {

        //1、创建线程池对象
        //public static ExecutorService newFixedThreadPool(int nThreads)
        ExecutorService pool = Executors.newFixedThreadPool(2);     // 表示线程池中最多允许2个线程在执行任务。


        //2、在线程池中存放线程:
        //Future<?> submit(Runnable task)   提交一个可运行的任务执行,并返回一个表示该任务的未来。

        MyRunnable myRunnable = new MyRunnable();      // 这不是一个线程对象!!!

        //将对象作为参数传入
        pool.submit(myRunnable);    //当myRunnable作为参数传入时,被底层封装成了一个线程对象并启动执行。    结果: pool-1-thread-1:1 ... 1:200
        pool.submit(myRunnable);    // 两个线程thread1、thread2也会抢cpu线权

        //提交的线程数超过线程池的数量( > 2)也会执行,只不过是当有空闲线程位置的时候再去执行。
        pool.submit(myRunnable);


        //3、想结束线程的运行,可不可以手动结束呢?如果可以,怎么结束?
        //void shutdown()   启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。
        pool.shutdown();

        //线程池已经被关闭了,不能再继续提交任务。若再提交,会报错。
        pool.submit(myRunnable);     // RejectedExecutionException
    }
}
3、线程安全问题原因(缺一不可):
     (1)是否存在多线程环境。
     (2)是否存在共享数据/共享变量。
     (3)是否有多条语句操作着共享数据/共享变量。

     解决线程安全问题方法: 同步安全机制。


4、同步安全机制:

     方案一: 同步代码块
         格式:
             synchronized(对象){
                 需要同步的代码;
             }

     方案二: 同步方法
         格式:
             public synchronized void xxx(){}

     方案二: 加Lock锁
         Lock(接口)
         具体的子类:Class ReentrantLock

             格式:
                 lock() 加锁
                 unlock() 释放锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值