Java高级---多线程

Java高级—多线程

一、线程的概念

再讲线程之前,那么又又又又又又又要提起进程。(这个又字看多了,好像不认识了)

进程:

程序的一次的执行过程。

线程:

操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

二、线程的实现和方法

Thread thread = new Thread("线程1");//线程创建
System.out.println(thread.getName());//获取线程名称
thread.start();			//线程启动
thread.setPriority(8);  //设置优先级 1~10
thread.getState();		//获取线程状态
thread.isAlive();		//查看线程是否运行

三、多线程的实现方式

实现多线程的方式:
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口
4、线程池

1、继承Thread类
重写run方法

public class MyThread extends Thread{
    public void run(){      //线程运行的主体
        for (int i = 1; i <= 20; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();

        thread.start();     //真正意义上的多线程,因为run还是main主线程调用
        thread2.start();

    }
}

2、实现Runnable接口
重写run方法

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println(i+".你好,来自线程"+Thread.currentThread().getName());
            Thread.yield();     //礼让:放弃本次就会,重写竞争
        }
    }

    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread thread = new Thread(mr,"子线程1");
        Thread thread2 = new Thread(mr,"子线程2");
        thread.setPriority(1);      //设置优先级
        thread.setPriority(10);

        thread.start();
        thread2.start();
    }
}

3、实现Callable接口
重写call方法
由于run方法没有返回值,想要将返回值返回给客户程序就需要通过call方法来实现

使用Callable接口实现多线程的步骤
1.第一步:创建Callable子类的实例化对象

2.第二步:创建FutureTask对象,并将Callable对象传入FutureTask的构造方法中(注意:FutureTask实现了Runnable接口和Future接口)

3.第三步:实例化Thread对象,并在构造方法中传入FurureTask对象

4.第四步:启动线程

FutureTask ft = new FutureTask(Callable接口的实现类)
Thread thread = new Thread(ft);
thread.start();
ft.get();//获取call()方法的返回值

public class MyCallable implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println("执行call方法");
        return "999";
    }

    public static void main(String[] args) {
        FutureTask<String> ft = new FutureTask(new MyCallable());
        Thread thread1 = new Thread(ft);
        thread1.start();
    }
}

4、线程池
1.newFixedThreadPool
固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。

该线程池中的线程数量始终不变,当有新任务提交时,线程池中有空闲线程则会立即执行,如果没有,则会暂存到阻塞队列。对于固定大小的线程池,不存在线程数量的变化。同时使用无界的LinkedBlockingQueue来存放执行的任务。当任务提交十分频繁的时候,LinkedBlockingQueue

迅速增大,存在着耗尽系统资源的问题。而且在线程池空闲时,即线程池中没有可运行任务时,它也不会释放工作线程,还会占用一定的系统资源,需要shutdown。

public class FixPoolDemo {

    private static Runnable getThread(final int i) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) {
        ExecutorService fixPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            fixPool.execute(getThread(i));
        }
        fixPool.shutdown();
    }
}

2.newSingleThreadExecutor
单个线程线程池,只有一个线程的线程池,阻塞队列使用的是LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,待空闲时再去执行。按照先入先出的顺序执行任务。

public class CachePool {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                }catch (Exception e){

                }
                System.out.println(i);
            }
        };
    }

    public static  void main(String args[]){
        ExecutorService cachePool = Executors.newCachedThreadPool();
        for (int i=1;i<=10;i++){
            cachePool.execute(getThread(i));
        }
    }
}

3.newCachedThreadPool
缓存线程池,缓存的线程默认存活60秒。线程的核心池corePoolSize大小为0,核心池最大为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。是一个直接提交的阻塞队列, 他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。

public class SingPoolDemo {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) throws InterruptedException {
        ExecutorService singPool = Executors.newSingleThreadExecutor();
        for (int i=0;i<10;i++){
            singPool.execute(getThread(i));
        }
        singPool.shutdown();
    }

4.newScheduledThreadPool
定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。
scheduleAtFixedRate:是以固定的频率去执行任务,周期是指每次执行任务成功执行之间的间隔。
schedultWithFixedDelay:是以固定的延时去执行任务,延时是指上一次执行成功之后和下一次开始执行的之前的时间。

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

        ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(4000);
                    System.out.println(Thread.currentThread().getId() + "执行了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);
    }
}

三、同步机制

synchronized

举个例子:

买票:
非同步

public class BuyTicket extends Thread{
    int left = 15;  //总共15张票,余票
    int used = 0;   //已卖票数

    public synchronized void buy(){
        
    }
    public void run(){
        //取票
        while (left>0){
            left--;
            used++;
            try {
                Thread.sleep(100);      //模拟网络延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.print(Thread.currentThread().getName()+"成功购买");
            System.out.println("当前已卖"+used+"张"+"余票数:"+left);
        }
    }

    public static void main(String[] args) {
        BuyTicket bt = new BuyTicket();
        Thread t1 = new Thread(bt,"奥网城");
        Thread t2 = new Thread(bt,"ssssssssssss");
        Thread t3 = new Thread(bt,"Tom");

        t1.start();
        t2.start();
        t3.start();
    }
}

【结果:会出现多个人买同一张票】

同步

public class BuyTicket extends Thread{
    int left = 15;  //总共15张票,余票
    int used = 0;   //已卖票数

    public synchronized boolean buy(){
        if(left<=0)return false;
        left--;
        used++;
        System.out.print(Thread.currentThread().getName()+"成功购买");
        System.out.println(",当前已卖"+used+"张"+",余票数:"+left);
        return true;
    }

    public void run(){
        //取票
        while (true){
            if(!buy())
                break;
            try {
                Thread.sleep(100);  //网络延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

结果显示
在这里插入图片描述

1、synchronized和lock的用法区别

synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。

lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。

2、synchronized和lock性能区别

synchronized是托管给JVM执行的,
而lock是java写的控制锁的代码。

在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。

但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。

2种机制的具体区别:
synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。

而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。

3、synchronized和lock用途区别

synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。

1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候

猜你喜欢

转载自blog.csdn.net/qq_43288259/article/details/113548253