java---线程

目录

1.什么是线程 

2.线程的组成

线程的状态:

 4.线程的安全问题 

5.同步操作

6.线程死锁

7.线程通信

 8.线程池

 9.使用最原始的方式创建线程池

10.创建线程的第三种方式(Callable)

11.手动锁


1.什么是线程 

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

线程,被称为轻量进程(light weight processes),进程中的一条执行路径,也是CPU的基本调度单位。一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。

进程与线程的区别:

1. 进程是操作系统资源分配的基本单位,而线程是 CPU 的基本调度单位。
2. 一个程序运行后至少有一个进程。
3. 一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义。
4. 进程间不能共享数据段地址,但是同进程的线程之间可以。

2.线程的组成

任何一个线程都具有基本的组成部分 :  

CPU时间片: 操作系统(OS)会为每个线程分配执行时间 

运行数据:堆空间: 存储线程需要的对象,多个线程可以共享堆中的数据。

栈空间:  存储线程需使用的局部变量,每个线程都拥有独立的栈。线程的逻辑代码.

线程的特点:

1、线程抢占式执行

        效率高
        可防止单一线程长时间独占CPU.
2. 在单核 CPU 中,宏观上同时执行,微观上顺序执行
线程的创建方式:
1. 【继承 Thread 类,重写 run 方法】
示例:使用线程Thread类实现4个窗口各卖100张票
Thread类:
public class MyThread extends Thread{

    private int ticket = 100;

    @Override
    public void run(){
        while (true){
            if(ticket>0){
                ticket--;
                System.out.println(Thread.currentThread().getName()+"卖了一张票,还有"+ticket+"张");
            }else {
                break;
            }
        }
    }
}

Test:

public class Test {

    public static void main(String[] args) {

        MyThread myThread1 = new MyThread();
        myThread1.setName("窗口a");

        MyThread myThread2 = new MyThread();
        myThread2.setName("窗口b");

        MyThread myThread3 = new MyThread();
        myThread3.setName("窗口c");

        MyThread myThread4 = new MyThread();
        myThread4.setName("窗口d");

        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
    }
}
2. 【 实现 Runnable 接口】
示例:
a.实现四个窗口共卖100 张票

        Thread类:

public class TicketRunnable implements Runnable{

    private int ticket = 100;

    @Override
    public void run() {
            while (true){
                //锁
                synchronized (this) {
                    if (ticket > 0) {
                        ticket--;
                        System.out.println(Thread.currentThread().getName() +"卖了一张票,还有"+ticket+"张");
                    } else {
                        break;
                    }
                }
            }
    }
}
Test类:
public class Test {
    public static void main(String[] args) {
        //创建任务对象
        TicketRunnable tr = new TicketRunnable();

        //创建线程对象并指定要执行的任务
        Thread t1 = new Thread(tr,"窗口a");
        Thread t2 = new Thread(tr,"窗口b");
        Thread t3 = new Thread(tr,"窗口c");
        Thread t4 = new Thread(tr,"窗口d");

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

    }
}

b.你和你女朋友公用一张银行卡,你向卡中存钱,你女朋友从卡中取钱,使用线程模拟过程!

public class TestBank {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard(0);
        SaveMoney save = new SaveMoney(bankCard);
        TakeMoney take = new TakeMoney(bankCard);
        Thread t1 = new Thread(save,"张三");
        Thread t2 = new Thread(take,"李四");
        t1.start();
        t2.start();

    }
}

//取钱任务
class TakeMoney implements Runnable{
    private BankCard card;
    public TakeMoney(BankCard card){
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if(card.getBalance()>=1000) {
                card.setBalance(card.getBalance() - 1000);
                System.out.println(Thread.currentThread().getName() + "取了卡中1000元,卡中余额为:" + card.getBalance());
            }else {
                System.out.println("卡中没钱了,赶紧存钱");
                i--;
            }
        }
    }
}

//存钱任务
class SaveMoney implements Runnable{
    private BankCard card;
    public SaveMoney(BankCard card){
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            card.setBalance(card.getBalance()+1000);
            System.out.println(Thread.currentThread().getName()+"向卡中存了1000元,卡中余额为:"+card.getBalance());
        }
    }
}

//抽取类:--银行卡类
class BankCard{
    private double balance;
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    public BankCard(double balance) {
        this.balance = balance;
    }
}
3.  实现 Callable 接口
获取和设置线程的名称
获取线程 ID 和线程名称
1. Thread 的子类中调用 this.getId () this.getName ()
2. 使用 Thread.currentThread (). getId () 和Thread.currentThread(). getName ()
修改线程名称
1. 调用线程对象的 setName () 方法
2. 使用线程子类的构造方法赋值

线程的状态:

常见的方法:

休眠 :
    public static void sleep(long millis )  当前线程主动休眠millis 毫秒。
public class TestSleep {
    public static void main(String[] args) {
        T t = new T();
        t.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main线程========"+i);
        }
    }
}

class T extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(100); //休眠当前线程 单位 毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("=========="+i);
        }
    }
}
放弃 :
     public static void yield() 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
public class TestYield {
    public static void main(String[] args) {
        T2 t01 = new T2();
        T2 t02 = new T2();
        t01.start();
        t02.start();
    }
}
class T2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            //当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
            Thread.yield(); //可能会导致t01与t02交替的频率变高
            System.out.println(Thread.currentThread().getName()+"==============="+i);
        }
    }
}
加入 :
     public final void join()  允许其他线程加入到当前线程中
//t1,t2,t3 如何保证这三个线程有序得值,t1执行完t2执行,t2执行完t3执行
public class TestJoin {
    public static void main(String[] args) {
        try {
            T3 t3 = new T3();
            t3.start();

            //将t3线程加入当前main线程中,直到t3执行完后,执行main
            t3.join();

            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+"========="+i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class T3 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"==========="+i);
        }
    }
}

优先级 :
      线程对象 . setPriority ()  线程优先级1-10 ,默认为 5 ,优先级越高,表示获取 CPU 的概率越高。
守护线程 :
     线程对象 . setDaemon (true); 设置为守护线程。线程有两类: 用户线程(前台线程)和守护线程(后台线程) 如果程序中所有前台线程都执行完毕了,后台线程也会自动结束。垃圾回收线程属于守护线程。
public class TestDaemon {
    public static void main(String[] args) {
        T4 t4 = new T4();

        //设置t4为守护线程,当前台线程执行完后,守护线程也自动结束(没执行完也会强制结束)
        t4.setDaemon(true);
        t4.start();

        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"==========="+i);
        }
    }
}
class T4 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"==========="+i);
        }
    }
}

线程的等待状态

 4.线程的安全问题 

public class TestSaft {
    private static String[] arr = new String[5];
    private static int index = 0;
    public static void main(String[] args) throws InterruptedException {
        //匿名对象
        Runnable hello = new Runnable() {
            @Override
            public void run() {
                if (arr[index]==null){
                    arr[index]="hello";
                    index++;
                }
            }
        };
 
        Runnable world = new Runnable() {
            @Override
            public void run() {
                if (arr[index]==null){
                    arr[index]="world";
                    index++;
                }
            }
        };
 
        Thread t1 = new Thread(hello);
        Thread t2 = new Thread(world);
 
        t1.start();
        t2.start();
 
        t1.join();
        t2.join();
        System.out.println(Arrays.asList(arr));
        //结果会有以下四种
        //1. [hello, world, null, null, null]
        //2. [world, hello, null, null, null]
        //3. [hello, null, null, null, null]
        //4. [world, null, null, null, null]
    }
}

5.同步操作

同步代码块:

 synchronized(临界资源对象){        //对临界资源对象加锁

        //代码(原子操作)

}

注意:
  • 每个对象都有一个互斥锁标记,用来分配给线程的
  • 只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块
  • 线程退出同步代码块时,会释放相应的互斥锁标记

6.线程死锁

当A线程拥有锁资源a时,这时A线程需要锁资源b, 而B线程拥有锁资源b,这时B线程需要锁资源a, 这样会导致A等待B线程释放资源b, B线程等待A线程释放锁资源a。 从而二个处于永久等待。从而操作死锁。

例子: 情人节---两个情人去餐厅吃饭---必须具有两个筷子。男方拥有一根筷子,女方拥有另一个筷子。

定义两个锁:

public class LockObject {
    public static Object a = new Object();
    public static Object b = new Object();
}

男方:

public class Boy extends Thread{
    @Override
    public void run() {
        synchronized (LockObject.a){
            System.out.println(Thread.currentThread().getName()+"得到第一根筷子a");
            synchronized (LockObject.b){
                System.out.println(Thread.currentThread().getName()+"得到第二根筷子b");
                System.out.println("可以吃饭了");
            }
        }
    }
}

女方:

public class Girl extends Thread{
    @Override
    public void run() {
        synchronized (LockObject.b){
            System.out.println(Thread.currentThread().getName()+"得到第一根筷子b");
            synchronized (LockObject.a){
                System.out.println(Thread.currentThread().getName()+"得到第二根筷子a");
                System.out.println("可以吃饭了");
            }
        }
    }
}

 Test测试类:

public class TestDeadlock {
    public static void main(String[] args) {
        Boy boy = new Boy();
        Girl girl = new Girl();

        boy.setName("张三");
        girl.setName("李四");

        boy.start();
        girl.start();
    }
}

 运行结果:

可以看到二者僵持住了,没有进行下一步,也没有结束运行,死锁了。 

操作死锁的原因:  锁与锁之间有嵌套导致。

如何解决死锁:

1. 尽量减少锁得嵌套。
2. 可以使用一些安全类。
3. 可以使用Lock中得枷锁,设置枷锁时间。

7.线程通信

 线程通信中使用得方法有哪些?

例子: 存钱和取钱。  

 银行卡类:

public class BankCard {
    private double balance;
    //true:表示有钱  false:表示没钱
    private boolean flag = false;

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    //存钱
    public synchronized void save(double money){
        if(flag==true){
            try{
                //进入等待队列
                this.wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        this.balance = this.balance+money;
        System.out.println(Thread.currentThread().getName()+"存了"+money+"余额为:"+this.balance);

        flag = true;

        //唤醒等待队列中的某个线程   this.notifyAll() 唤醒等待队列中的所有线程
        this.notify();
    }

    //取钱
    public synchronized void take(double money){
        if(flag==false){
            try{
                this.wait();//使该线程进入等待队列
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        this.balance = this.balance-money;
        System.out.println(Thread.currentThread().getName()+"取了"+money+"余额为:"+this.balance);

        flag = false;

        this.notify();
    }
}

男方

public class Boy implements Runnable{
    private BankCard bankCard;
    public Boy(BankCard bankCard){
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.save(1000);
        }
    }
}

女方

public class Girl implements Runnable{
    private BankCard bankCard;
    public Girl(BankCard bankCard){
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.take(1000);
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();

        Boy boy = new Boy(bankCard);
        Girl girl = new Girl(bankCard);

        Thread t1 = new Thread(boy,"张三");
        Thread t2 = new Thread(girl,"李四");

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

运行结果

先存钱---再取钱

思考:   sleep和wait得区别?

1.所在得类不同。sleep属于Thread类,wait属于Object类。
2.使用的地方: sleep可以使用再任何代码块。wait只能再同步代码块中。
3.是否释放锁资源: sleep不释放锁资源,wait会释放锁资源。
4.sleep时间到了自动唤醒,wait必须需要使用notify和notifyAll唤醒

notify和notyfyAll区别?

 8.线程池

什么是线程池?

该池子中预先存储若干个线程对象。整个池子就是线程池。

线程池的创建方式有哪些?

所有的线程池---封装了一个父接口---java.util.concurrent.Executor.

​ 它的实现接口: ExecutorService.

有一个工具类。Executors可以帮你创建相应的线程池。

[1] 创建单一线程池 newSingleThreadExecutor()

[2] 创建定长线程池。newFixedThreadPool(n);

[3] 创建可变线程池. newCachedThreadPool()

[4] 创建延迟线程池 .newScheduledThreadPool(n);

单一线程池 newSingleThreadExecutor()

public class Test01 {
    public static void main(String[] args) {
        // 单一线程池:适用于 队列要求线程有序执行
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 5; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~~");
                }
            });
        }

        //关闭线程池。需要等待线程池中任务执行完毕后才会关闭。
        executorService.shutdown();
    }
}

定长线程池。newFixedThreadPool(n);
public static void main(String[] args) {
      

        //固定长度的线程池对象
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~~");
                }
            });
        }

        //关闭线程池。需要等待线程池中任务执行完毕后才会关闭。
        executorService.shutdown();
    }
}

可变线程池. newCachedThreadPool()

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

        //可变长度的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 5; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~~");
                }
            });
        }

        //关闭线程池。需要等待线程池中任务执行完毕后才会关闭。
        executorService.shutdown();
    }
}

延迟线程池 .newScheduledThreadPool(n);     延迟时间结束后返回结果

public class Test01 {
    public static void main(String[] args) {
        
        //延迟线程池对象
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);

        for (int i = 0; i < 5; i++) {
            executorService.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~~");
                }
            },10, TimeUnit.SECONDS);//设置延迟时间 10 单位秒
        }

        //关闭线程池。需要等待线程池中任务执行完毕后才会关闭。
        executorService.shutdown();
    }
}

 

 9.使用最原始的方式创建线程池

上面讲解的使用Executors创建线程池的方式,都是使用底层ThreadPoolExecutor,而阿里开发手册,建议使用最原始的方式。

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

ThreadPoolExecutor 执行流程:

  • 线程数小于核心线程数时,创建线程。
  • 线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  • 线程数大于等于核心线程数,且任务队列已满:若线程数小于最大线程数,创建线程;若线程数等于最大线程数,抛出异常,拒绝任务。

ThreadPoolExecutor 创建的线程池执行流程:

10.创建线程的第三种方式(Callable)

实现Callable接口,它和实现Runnable接口差不多,只是该接口种的方法有返回值和异常抛出。

public class Test {
    public static void main(String[] args) throws Exception{
        //自建创建线程对象并提交Callable类型的任务是比较麻烦的,需要封装到一个FutureTask类种, 建议使用线程池来提交任务

        My my = new My();
        FutureTask futureTask = new FutureTask(my);
        Thread t1 = new Thread(futureTask);
        t1.start();

        System.out.println(futureTask.get());

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Future<Integer> future = executorService.submit(my);

        //需要等线程执行完毕后,才会把结果返回给该变量
        Integer sum = future.get();

    }
}

class My implements Callable<Integer>{

    //1-100的和
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i=1;i<=100;i++){
            sum+=i;
        }
        return sum;
    }
}

11.手动锁

Lock它是手动锁的父接口,它下面有很多实现类。

lock()方法。

unlock()释放锁资源,放在finally中

public class Test {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        Thread t1 = new Thread(ticket,"窗口A");
        Thread t2 = new Thread(ticket,"窗口B");
        Thread t3 = new Thread(ticket,"窗口C");
        Thread t4 = new Thread(ticket,"窗口D");

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

class Ticket implements Runnable{

    private int ticket = 100;
    Lock s = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                s.lock();//查看释放获取锁资源
                if (ticket > 0) {
                    --ticket;
                    System.out.println(Thread.currentThread().getName() + "卖了一张,剩余:" + ticket + "张");

                } else {
                    break;
                }
            }finally {
                s.unlock(); //释放锁
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naaaaa.a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值