Java基础知识——多线程

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、线程简介

多线程是指一个进程在执行过程中可以产生多个更小的程序单元,这些更小的单元称为线程,这些线程可以同时存在,同时运行,一个进程可 能包含多个同时执行的线程。

  • 线程就是独立的执行路径
  • 在程序运行时,及时没有自己创建线程,后台也会有很多线程
  • main()成为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  • 线程会带来额外开销,如cpu调度时间、并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

二、线程创建

Thread类、Runnable接口

1、继承Thread类
  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
 public static void main(String[] args){
        StartThread t = new StartThread();
        t.start();
    }
    
    public static class StartThread extends Thread {
        //线程入口点
        public void run(){
            //线程体
            for(int i = 0 ; i < 20 ; i++)
                System.out.println("我在听---");
        }
    }
2、实现Runnable接口
  • 定义类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,执行线程要丢入runnable接口的实现类,调用start()方法启动线程
    public static void main(String[] args){
        //创建runnable接口的实现类对象
        TestThread testThread = new TestThread();
        //创建线程对象,通过线程对象来开启线程
        Thread thread = new Thread(testThread);

        thread.start();
    }
    public static class TestThread implements Runnable{
        public void run(){
            for(int i = 0 ; i < 10 ;i++)
                System.out.println("多线程");
        }
    }

三、线程状态

  • 运行中(RUNNING):就绪的线程获得 CPU 的时间片就变为运行中。
  • 阻塞(BLOCKED):线程等待获得锁
  • 等待(WAITING):接收事件通知后或系统中断后进入等待
  • 超时(TIMED_WAITING):等待指定时间后会自行返回
  • 终止(TERMINATED):线程已执行完毕

线程方法:

  • setPriority(int newPriority):更改线程的优先级
  • static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠
  • void join():等待该线程终止
  • static void yield():暂停当前正在执行的线程对象,并执行其他线程
  • void interrupt():中断线程
  • boolean isAlive():测试线程是否处于活动状态
1、停止线程

用一个标志位进行终止变量,当flag=false的时候,则终止线程运行

package thread;

public class task03 {
    public static void main(String[] args){
        TestStop testStop = new TestStop();

        new Thread(testStop).start();

        for(int i = 0 ; i < 1000 ; i++){
            if(i==900)
                testStop.stop();
        }
    }

    public static class TestStop implements Runnable{
        //设置一个标志位
        private boolean flag = true;

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

        public void stop(){
            this.flag = false;
        }
    }
}

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

public class task04 {
    //模拟倒计时
    public static void main(String[] args){
        try{
            tenDown();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    private static void tenDown() throws InterruptedException{
        int num = 10;
        while(true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num<=0)
                break;
        }
    }


}

3、线程礼让
  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu重新调度,但礼让不一定成功
package thread;

public class task05 {
    public static void main(String[] args){
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }

}

class MyYield implements Runnable {
    public void run(){
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

4、线程强制执行
package thread;

public class test06 {
    public static void main(String[] args) throws InterruptedException{
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for(int i = 0 ; i < 500 ;i++){
            if(i==200){
                thread.join();
            }
            System.out.println("main");
        }
    }

    static class TestJoin implements Runnable{
        public void run(){
            for(int i = 0 ; i < 1000 ;i++)
                System.out.println("插队");
        }
    }
}

5、线程优先级
package thread;

import com.company.Task10_01;

public class test07 {
    public static void main(String[] args){
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);

        //先设置优先级再启动
        t1.setPriority(1);
        t1.start();

        t2.setPriority(Thread.MAX_PRIORITY);
        t2.start();

        t3.setPriority(6);
        t3.start();
    }

    static class MyPriority implements Runnable{
        public void run(){
            System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        }
    }
}

四、线程同步

1、synchronized

由于一个进程的多个线程共享一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。

  • synchronized方法:锁的是类本身
  • synchronized块:可以锁任何对象,如synchroniced(account){}锁的是账户这个对象
2、lock
package thread;

import java.util.concurrent.locks.ReentrantLock;

public class test08 {
    public static void main(String[] args){
        TestLock testLock = new TestLock();
        new Thread(testLock).start();
        new Thread(testLock).start();
        new Thread(testLock).start();
    }
}

class TestLock implements Runnable {
    int ticketNums = 10;

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

    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();
            }
        }
    }
}
3、synchronized和lock对比
  • Lock是显示锁(手动开启和关闭锁),snchronized是隐式锁(出了作用域自动释放)
  • Lock只有代码块锁,synchronized有代码块锁和方法锁

五、线程通信

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

生产者消费者问题:利用缓冲区解决

package thread;

import com.company.Task4_3;

public class test09 {
    public static void main(String[] args){
        SynContainer synContainer = new SynContainer();
        new Producer(synContainer).start();
        new Customer(synContainer).start();
    }
}

//生产者
class Producer extends Thread {
    SynContainer container;
    public Producer(SynContainer container){
        this.container = container;
    }
    //生产
    public void run(){
        for(int i = 0 ; i < 100 ; i++){
            System.out.println("生产了"+i+"只鸡");
            container.push(new Chicken(i));
        }
    }
}

//消费者
class Customer extends Thread {
    SynContainer container;
    public Customer(SynContainer container){
        this.container = container;
    }
    //消费
    public void run(){
        for(int i = 0 ; i < 100 ; i++){
            System.out.println("消费了-->"+container.pop()+"只鸡");
        }
    }
}

//产品
class Chicken{
    int id;
    public Chicken(int id){
        this.id = id;
    }
}

//缓冲区
class SynContainer {
    //容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0 ;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {
        //如果容器满了,就要等待消费者消费
        if(count==chickens .length){
            try{
                this.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

        chickens[count] = chicken;
        count++;

        //可以通知消费者消费了
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
       if(count==0){
           try{
               this.wait();
           }catch (InterruptedException e){
               e.printStackTrace();
           }
       }

       count--;
       Chicken chicken = chickens[count];
       //通知生产者生产
       this.notifyAll();
        return chicken;
    }
}

六、线程池

ExecutorService:线程池接口

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • void shutdown():关闭连接池
package thread;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class test10 {
    public static void main(String[] args){
        //创建服务,创建线程池
        //参数为:线程池大小
        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 {
    public void run(){
        System.out.println(Thread.currentThread().getName());
    }
}

七、练习

1、熟悉线程生命周期方法。 开启四个线程,两个线程调用锁的 wait 方法,另外两个调用锁的 notify 方法,观察执 行结果并解释原因。

package thread;

import java.util.concurrent.locks.ReentrantLock;

public class task11_01 {
    public static void main(String[] args){
        TestLock1 testLock1 = new TestLock1();
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock1).start();
        new Thread(testLock1).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}

class TestLock1 implements Runnable {


    public void run(){
        System.out.println(Thread.currentThread().getName()+"开始运行");
        synchronized ("lock1"){
            try{
                "lock1".wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"结束运行");
    }
}

class TestLock2 implements Runnable {

    public void run(){
        System.out.println(Thread.currentThread().getName()+"开始运行");
        synchronized ("lock1"){
            "lock1".notify();
        }
        System.out.println(Thread.currentThread().getName()+"结束运行");
    }
}

在这里插入图片描述
在这里插入图片描述
结果:在同一个方法中,连续创建多个线程后,调用线程的start()方法的顺序并不能决定线程的执行顺序。但是利用wait()和notify()方法后,可以保证一定的顺序。只有调用thread3或者thread2中的notify()方法,thread0和thread1才能够结束执行

在 main 函数中开启一个子线程,如果想让 main 主线程在子线程执行完之后才继续执行,代码该怎么写?

package thread;

public class test11_02 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main函数开始执行");
        TestThread testThread = new TestThread();
        Thread thread = new Thread(testThread);
        thread.start();
        thread.join();
        System.out.println(Thread.currentThread().getName()+"正在执行");
    }
}

class TestThread implements Runnable {

        public void run(){
            System.out.println(Thread.currentThread().getName()+"正在执行");
        }

}

2、使用 synchronized 实现抢票程序:某商场做活动,有 100 部 iPhone 可以抽奖兑换。 现在在三个柜台同时兑换,要求所有柜台已兑换的 iPhone 数量加起来刚好是 100,既 不能多换,也不能少换。要求:用 Thread 类实现;用 Runnable 接口实现。

Thread类实现

package thread;

public class test11_03 {
    private static int num = 100;
    public static void main(String[] args){
        new TestThread().start();
        new TestThread().start();
        new TestThread().start();
    }
    static class TestThread extends Thread{
        public void run(){
            while(true){
                synchronized ("num"){
                    if(num>0){
                        System.out.println(Thread.currentThread().getName()+"在兑换第"+(101-num)+"台手机");
                        num--;
                    }
                }
            }
        }
    }
}

Runnable接口实现:

package thread;

public class test11_03 {
    private static int num = 100;
    public static void main(String[] args){
        TestThread testThread = new TestThread();
        new Thread(testThread).start();
        new Thread(testThread).start();
        new Thread(testThread).start();
    }
    static class TestThread implements Runnable{
        public void run(){
            while(true){
                synchronized ("num"){
                    if(num>0){
                        System.out.println(Thread.currentThread().getName()+"在兑换第"+(101-num)+"台手机");
                        num--;
                    }
                }
            }
        }
    }
}

3、使用 newScheduledThreadPool 线程池实现每隔 1 分钟打印一条消息

package thread;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class test11_04 {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        pool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("一分钟过去了");
            }
        }, 0, 1, TimeUnit.MINUTES);
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值