java线程

1.线程的创建

线程(Thread)是一个程序内部的一条执行流程

多线程的创建方式一:继承Thread

代码如下:

public class thread extends Thread {
    //必须重写run方法
    @Override
    public void run() {
        //描述线程的任务
        for (int i = 0; i < 5; i++) {
            System.out.println("线程是" + i);
        }
    }
}
public class main {
    public static void main(String[] args) {
        thread thread = new thread();
        //调用start方法
        thread.start();
        }
}

多线程的创建方式二:实现Runnable接口

public class myrunnable implements Runnable{
    @Override
    public void run() {
        //线程要执行的任务
        for (int i = 0; i < 5; i++) {
            System.out.println("线程1执行的i是"+i);
        }
    }
}
package src;

public class main {
    public static void main(String[] args) {
        //第一种
        myrunnable runnable = new myrunnable();
        new Thread(runnable).start();
        //第二种
        new Thread(new Runnable() {
            @Override
            public void run() {
                //线程要执行的任务
                for (int i = 0; i < 5; i++) {
                    System.out.println("线程2执行的i是"+i);
                }
            }
        }).start();
        //第三种
        new Thread(()->{
            //线程要执行的任务
            for (int i = 0; i < 5; i++) {
                System.out.println("线程3执行的i是"+i);
            }
        }).start();

        for (int i = 0; i < 5; i++) {
            System.out.println("main方法输出" + i);
        }
    }
}

多线程的创建方式三:实现Callable接口

、创建任务对象
        定义一个类实现Callable接口,重写 call 方法,封装要做的事情,和要返回的数据。
        把Callable类型的对象封装成 FutureTask (线程任务对象)。
、把线程任务对象交给 Thread 对象。
、调用 Thread 对象的 start 方法启动线程。
、线程执行完毕后、通过 FutureTask 对象的的 get 方法去获取线程任务执行的结果。
public class mycallable implements Callable  {
    private final int n ;

    public mycallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum=0;
        //获取i到n的累加
        for (int i = 1; i <= n; i++) {
            sum=sum+i;
        }
        return "获取i到n的累加值为"+sum;
    }
}
public class main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        mycallable callable = new mycallable(100);
        FutureTask<String> futureTask = new FutureTask<>(callable);
        new Thread(futureTask).start();
        String result = futureTask.get();
        System.out.println(result);

        for (int i = 0; i < 5; i++) {
            System.out.println("main方法输出" + i);
        }
    }
}

2.Thread的常用方法

3.线程安全

线程安全问题出现的原因?

        存在多个线程在同时执行

        同时访问一个共享资源

存在修改该共享资源

4.线程同步

线程同步的思想

        加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

1.同步代码块的方法

同步代码块作用:把访问共享资源的核心代码给上锁,以此保证线程安全。

synchronized(同步锁) {
          访问共享资源的核心代码
}

ps:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug

使用规范:

建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。

对于静态方法建议使用字节码(类名.class)对象作为锁对象。

    public static void test(){
        synchronized (src.account.class){

        }
    }

    public void quqian() {
        String name = Thread.currentThread().getName();
        synchronized (this){
            if (account>=100000){
                System.out.println(name+"取钱了"+account);
                this.account=account-100000;
                System.out.println(name+"余额剩下"+account);
            }else {
                System.out.println(name+"取钱但是不够");
            }
        }
        }

2.同步方法

作用:访问共享资源的核心方法给上锁,以此保证线程安全。

修饰符 synchronized 返回值类型 方法名称(形参列表) {
  操作共享资源的代码
}

原理 :每次只能一个线程进入执行完毕以后自动解锁,其他线程才可以进来执行

同步方法其实底层也是有隐式锁对象的 ,只是锁的范围是整个方法代码。
如果方法是实例方法:同步方法默认用 this 作为的锁对象。
如果方法是静态方法:同步方法默认用 类名.class 作为的锁对象。

是同步代码块好还是同步方法好一点?

        范围上:同步代码块锁的范围更小,同步方法锁的范围更大。

        可读性:同步方法更好。

    public static synchronized void test(){

    }

    public synchronized void quqian() {
        String name = Thread.currentThread().getName();
        if (account>=100000){
            System.out.println(name+"取钱了"+account);
            this.account=account-100000;
            System.out.println(name+"余额剩下"+account);
        }else {
            System.out.println(name+"取钱但是不够");
        }
    }

3.Lock锁

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。

Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

代码如下:

    //实例化锁
    private final Lock lk=new ReentrantLock();    
    public synchronized void quqian() {
        try{
            //加锁    
            lk.lock();
            String name = Thread.currentThread().getName();
            if (account>=100000){
                System.out.println(name+"取钱了"+account);
                this.account=account-100000;
                System.out.println(name+"余额剩下"+account);
            }else {
                System.out.println(name+"取钱但是不够");
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        finally {
            //解锁
            lk.unlock();
        }
    }

5.线程通信

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

线程通信的常见模型(生产者与消费者模型)

        生产者线程负责生产数据

        消费者线程负责消费生产者生产的数据。

        注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!

6.线程池

线程池就是一个可以复用线程的技术

不使用线程池的问题 

        用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,  而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

创建线程池

JDK 5.0起提供了代表线程池的接口:ExecutorService

        方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。

        方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。

1.方式一使用ExecutorService的实现类

ThreadPoolExecutor构造器

实例代码如下:

    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(3,5,5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

    }

1、临时线程什么时候创建?

新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

2、什么时候会开始拒绝新任务?

        核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

线程池处理Runnable任务

示例代码如下:

    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(3,5,4, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        myrunnable runnable = new myrunnable();
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        //线程等待
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        //新建线程
        pool.execute(runnable);
        pool.execute(runnable);
        //拒绝线程
        pool.execute(runnable);
        //关闭线程
        //等线程执行完毕关闭线程
        pool.shutdown();
        //不等线程执行完毕立即关闭线程
        //pool.shutdownNow();
    }

线程池处理Callable任务

callable相比runnable多了一个返回结果,调用submit函数即可获取。

public class factory {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = new ThreadPoolExecutor(3,5,4, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        myrunnable runnable = new myrunnable();

        callable callable = new callable();
        Future<String> future = pool.submit(callable);
        System.out.println(future.get());
    }
}

2.方式二:使用Executors(线程池的工具类)

是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。

注意 :这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。

不建议使用Executors里的方法,因为请求队列和创建线程数都是最大,容易OOM内存溢出。

7.并发和并行

8.线程的生命周期

        也就是线程从生到死的过程中,经历的各种状态及状态转换。

        理解线程这些状态有利于提升并发编程的理解能力。

Java线程的状态

        Java总共定义了6种状态

        6种状态都定义在Thread类的内部枚举类中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值