Java多线程(基础)

多线程

java线程的状态

在这里插入图片描述

创建线程的方式

1.继承Thread类
  • 多线程常用方法

    在这里插入图片描述

  • 代码演示

    public class MyThread extends Thread{
        public MyThread(String name){
            super(name); //1.执行父类Thread(String name)构造器,为当前线程设置名字了
        }
        @Override
        public void run() {
            //2.currentThread() 哪个线程执行它,它就会得到哪个线程对象。
            Thread t = Thread.currentThread();
            for (int i = 1; i <= 3; i++) {
                //3.getName() 获取线程名称
                System.out.println(t.getName() + "输出:" + i);
            }
        }
    }
    
    public class ThreadTest1 {
        public static void main(String[] args) {
            Thread t1 = new MyThread();
            t1.setName(String name) //设置线程名称;
            t1.start();
            System.out.println(t1.getName());  //Thread-0
    
            Thread t2 = new MyThread("2号线程");
            // t2.setName("2号线程");
            t2.start();
            System.out.println(t2.getName()); // Thread-1
    
            // 主线程对象的名字
            // 哪个线程执行它,它就会得到哪个线程对象。
            Thread m = Thread.currentThread();
            m.setName("最牛的线程");
            System.out.println(m.getName()); // main
    
            for (int i = 1; i <= 5; i++) {
                System.out.println(m.getName() + "线程输出:" + i);
            }
        }
    }
    
2.实现Runnable接口
  • 使用步骤

    1.先写一个Runnable接口的实现类,重写run方法(这里面就是线程要执行的代码)
    2.再创建一个Runnable实现类的对象
    3.创建一个Thread对象,把Runnable实现类的对象传递给Thread
    4.调用Thread对象的start()方法启动线程(启动后会自动执行Runnable里面的run方法)

  • 代码演示

    public class MyRunnable implements Runnable{
        // 2、重写runnable的run方法
        @Override
        public void run() {
            // 线程要执行的任务。
            for (int i = 1; i <= 5; i++) {
                System.out.println("子线程输出 ===》" + i);
            }
        }
    }
    
    public class ThreadTest2 {
        public static void main(String[] args) {
            // 3、创建任务对象。
            Runnable target = new MyRunnable();
            // 4、把任务对象交给一个线程对象处理。
            //  public Thread(Runnable target)
            new Thread(target).start();
    
            for (int i = 1; i <= 5; i++) {
                System.out.println("主线程main输出 ===》" + i);
            }
            
            //匿名内部类简化
             // 简化形式1:
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 5; i++) {
                        System.out.println("子线程2输出:" + i);
                    }
                }
            }).start();
    
            // 简化形式2:
            new Thread(() -> {
                    for (int i = 1; i <= 5; i++) {
                        System.out.println("子线程3输出:" + i);
                    }
            }).start();
    
            for (int i = 1; i <= 5; i++) {
                System.out.println("主线程main输出:" + i);
            }
        }
    }
    
3.实现Callable接口
  • 使用步骤

    1.先定义一个Callable接口的实现类,重写call方法(代码演示省略)
    2.创建Callable实现类的对象
    3.创建FutureTask类的对象,将Callable对象传递给FutureTask
    4.创建Thread对象,将Future对象传递给Thread
    5.调用Thread的start()方法启动线程(启动后会自动执行call方法)
    等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中

    6.调用FutrueTask对的get()方法获取返回结果

  • 代码演示

    public class ThreadTest3 {
        public static void main(String[] args) throws Exception {
            // 3、创建一个Callable的对象
            Callable<String> call = new MyCallable(100);
            // 4、把Callable的对象封装成一个FutureTask对象(任务对象)
            // 未来任务对象的作用?
            // 1、是一个任务对象,实现了Runnable对象.
            // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。
            FutureTask<String> f1  = new FutureTask<>(call);
            // 5、把任务对象交给一个Thread对象
            new Thread(f1).start();
    
    
            Callable<String> call2 = new MyCallable(200);
            FutureTask<String> f2  = new FutureTask<>(call2);
            new Thread(f2).start();
    
    
            // 6、获取线程执行完毕后返回的结果。
            // 注意:如果执行到这儿,假如上面的线程还没有执行完毕
            // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。
            String rs = f1.get();
            System.out.println(rs);
    
            String rs2 = f2.get();
            System.out.println(rs2);
        }
    }
    
4创建线程池
  • ThreadPoolExecutor构造器及核心参数

    ExecutorService pool = new ThreadPoolExecutor(
        int corePoolSize,	//核心线程数
        int maximumPoolSize,  //最大线程数。   临时线程数=最大线程数-核心线程数=5-3=2
        long keepAliveTime,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
        TimeUnit unit,//临时线程存活时间,时间单位(秒)
        BlockingQueue<Runnable> workQueue, //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
        ThreadFactory threadFactory, //用于创建线程的工厂对象
        RejectExecutionHandler handler //拒绝策略
    );
    
线程池执行Runnable任务
  • 使用execute方法来执行Runnable任务

  • 代码演示

    ExecutorService pool = new ThreadPoolExecutor(
        3,	//核心线程数有3个
        5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
        8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
        TimeUnit.SECONDS,//时间单位(秒)
        new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
        Executors.defaultThreadFactory(), //用于创建线程的工厂对象
        new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
    );
    
    Runnable target = new MyRunnable();
    pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
    ......            //继续添加任务
    
线程池执行Callable任务
  • 使用submit方法来执行Callable任务

  • 代码演示

    public class ThreadPoolTest2 {
        public static void main(String[] args) throws Exception {
            // 1、通过ThreadPoolExecutor创建一个线程池对象。
            ExecutorService pool = new ThreadPoolExecutor(
                3,
                5,
                8,
                TimeUnit.SECONDS, 
                new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    
            // 2、使用线程处理Callable任务。
            Future<String> f1 = pool.submit(new MyCallable(100));
            Future<String> f2 = pool.submit(new MyCallable(200));
    
            // 3、执行完Callable任务后,需要获取返回结果。
            System.out.println(f1.get());
            System.out.println(f2.get());
        }
    }
    

线程安全问题

1.同步代码块
//锁对象:必须是一个唯一的对象(同一个地址)
synchronized(锁对象){
    //...访问共享数据的代码...
}
  • 注意点:

​ 1.建议把共享资源作为锁对象, 不要将随便无关的对象当做锁对象
​ 2.对于实例方法,建议使用this作为锁对象
​ 3.对于静态方法,建议把类的字节码(类名.class)当做锁对象

2.同步方法

概念:把整个方法给锁住,一个线程调用这个方法,另一个线程调用的时候就执行不了,只有等上一个线程调用结束,下一个线程调用才能继续执行。

public synchronized void drawMoney(double money) {
    ......   //方法体
}
  • 同步方法也是有锁对象,只不过这个锁对象没有显示的写出来而已。
    1.对于实例方法,锁对象其实是this(也就是方法的调用者)
    2.对于静态方法,锁对象时类的字节码对象(类名.class)
3.Lock锁
1.首先在成员变量位子,需要创建一个Lock接口的实现类对象(这个对象就是锁对象)
	private final Lock lk = new ReentrantLock();
2.在需要上锁的地方加入下面的代码
	 lk.lock(); // 加锁
	 //...中间是被锁住的代码...
	 lk.unlock(); // 解锁

没有显示的写出来而已。
1.对于实例方法,锁对象其实是this(也就是方法的调用者)
2.对于静态方法,锁对象时类的字节码对象(类名.class)

3.Lock锁
1.首先在成员变量位子,需要创建一个Lock接口的实现类对象(这个对象就是锁对象)
	private final Lock lk = new ReentrantLock();
2.在需要上锁的地方加入下面的代码
	 lk.lock(); // 加锁
	 //...中间是被锁住的代码...
	 lk.unlock(); // 解锁
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值