JAVA-线程

一、进程、线程

        1.进程:正在运行的程序,是操作系统分配资源的基本单位

                目前操作系统都是支持多进程,介意同时执行多个进程,通过进程id区分。

        2.线程:轻量级进程 是进程的一个执行路径也是CPU的基本调度单位,进程是多个线程组成。 一个进程至少有一个线程。

        进程间不能共享数据地址,但同进程的线程可以。

二、线程的组成

        cpu时间片、内存空间、线程的逻辑代码。

三、线程执行特点

        抢占式执行,结果具有随机性;

        在单核CPU中宏观伤同时执行,微观上顺序执行、多核cpu可实现真正的并发执行。

四、创建线程

        1.继承Thread类重写run方法 

        2.实现Runnable接口

        3.线程池创建线程  

                Executors工厂类  newFixedThreadPool(int nThreads)  newCachedThreadPool()

        4.Callable接口 具有泛型返回值,可以声明异常

                public interface Callable<V>{ public V call() throws Exeption;}

public class MyThread extends Thread{
    MyThread(String name){
        super.setName(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            // 1. 获取线程名 和 线程ID  只适用于继承Thread类的方法使用
            // this.getId() this.getName()
            // 2. Thread.currentThread().getId() getName() [推荐]
            //  获取当前执行的线程的id和名字
            System.out.println(this.getId() + ":" + this.getName() + "子线程");
        }
    }
}



public class TestMyThread {
    public static void main(String[] args) {
        MyThread mT = new MyThread("AAAAAAAAAAAA");
        // 修改线程名
        mT.setName("a");
        // 还可通过构造函数传递名
        mT.start(); // 不要使用run 不然不是多线程
        for (int i = 0; i < 100; i++) {
            System.out.println("main线程");
        }
    }
}
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "----------------------");
        }
    }
}


public class TestRunnable {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread th = new Thread(mr);
        th.start();
    }
}

案例一、卖票

        需求:四个窗口各卖100张票

public class SaleTicket extends Thread {
    SaleTicket(String name){
        super.setName(name);
    }
    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            if(ticket <= 0){
                break;
            }
            System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
            ticket--;
        }
    }

}

五、线程的状态(基本)

                                                    -----等待状态------

                                           到期 |                  sleep  |

        new(初始状态)------> 就绪状态 ------> 运行状态 -------->终止状态

                                                   |                            |

                                                    -----阻塞状态-------

六、线程方法

        1.休眠  sleep(long millis)

                     当前线程主动休眠millis毫秒释放了cpu 不再争抢CPU

        2. 放弃yield()

                      当前线程主动放弃cpu时间片回到就绪状态竞争下一次时间片

        3. 加入 join()

                        允许其他线程加入到当前线程中。当前线程会阻塞,直到加入线程执行完毕。

        4.优先级  setPriority(int)

                        线程优先级 1-10 默认为5 优先级越高 表示获取CPU机会越多

        5.线程打断 interrupt()

                        打断线程抛出InterruptedException异常

        6.守护线程 setDaemon(true) start之前设置

                        用户线程(前台线程) 守护线程(后台线程)

                        如果程序中所有前台线程都执行完毕了 后台线程会自动结束

public class TestThread2 {
    public static void main(String[] args) {
        DaemonThread dT = new DaemonThread("a");
        dT.setDaemon(true); // 守护线程
        dT.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程" + " ---------- "+ i);
        }
    }
    static class DaemonThread extends Thread{
        DaemonThread(String name){
            super.setName(name);
        }
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                System.out.println(Thread.currentThread().getName() + "----" + i);
            }
        }
    }
}

七、线程同步

        同步:多个线程一个一个等待执行

        异步:线程之间不需要等待执行 

        同步代码块:

                synchrenized(锁对象){  // 对临界共享资源对象加锁

                        // 同步代码

                }

        注:任何引用类型对象都可以作为锁,一般使用临界资源或唯一应用类型对象作为锁。每个对象都有一个互斥锁标记,只是拥有对象互斥锁标记的线程,才能进入同步代码块。线程退出同步代码块时,会释放相应的互斥锁标记。

        同步方法: 如果是非静态方法 锁是this 如果是静态方法 时  类名.class

                synchrenized 返回值类型  方法名称(形参列表){

                        同步代码

                }

八、死锁

        当第一个线程拥有A对象锁标记 并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。

九、 线程通信

        等待:

        public final void wait()

                public final void wait(long timeout)

                必须在对obj加锁的同步代码块中

                调用obj.wait()时 此线程会释放拥有的所有锁标记 释放cpu进入等待队列。

        通知:

                public final void notify() 从等待队列中随机唤醒一个

                public final void notfyAll() 唤醒所有等待线程

                必须在对obj加锁的同步代码块中

                从obj的等待队列中随机唤醒一个或全部线程。

       案例  使用线程通信 实现存一次 取一次

package day04;

public class BankCard {
    private double money;
    private boolean flag; // true 有钱 可以取 不能存

    public synchronized void save(double m){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        this.money = this.money + m;
        System.out.println(Thread.currentThread().getName() + "存了" + m + "余额是:" + this.money);
        flag = true;
        this.notify();

    }

    public synchronized void take(double m){
        if(!flag){ // false  没钱不能取
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        this.money = this.money - m;
        System.out.println(Thread.currentThread().getName() + "取了" + m + "余额是:" + this.money);
        flag = false;
        this.notify();

    }
}

package day04;

public class Save implements  Runnable{

    private BankCard bankC;
    Save(BankCard bc){
        this.bankC = bc;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankC.save(1000);
        }
    }
}


package day04;

public class Take implements  Runnable{

    private BankCard bankC;
    Take(BankCard bc){
        this.bankC = bc;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankC.take(1000);
        }
    }
}


package day04;

public class TestBankCard {
    public static void main(String[] args) {
        BankCard bc = new BankCard();
        Save s = new Save(bc);
        Take t = new Take(bc);
        Thread ts = new Thread(s);
        Thread tt = new Thread(t);
        ts.start();
        tt.start();
    }
}

十、生产者消费者

十一、线程池

        线程容器,可设定线程分配的数量上限。

        将预先创建的线程对象存入池中,并重用线程池中的线程对象。

        避免频繁的创建和销毁。

        常用的线程池接口和类  (java.util.concurrent)

        Executor: 线程池的顶级接口

        ExecutorService :线程池接口  可通过submit提交任务代码

        Executors工厂类:通过此类可以获得一个线程池

                通过newFixedThread(int  nThreads)获取固定数量的线程池

                通过newCachedThreadPool() 获得动态数量的线程池(不够了就创建)

                通过newSingleThreadExecutor(); 创建单线程池

                通过newScheduleThreadPool(int nThreads) 调度线程池

                通过 newWorkStealingPool   采用工作窃取算法  双端队列  先查看自己的队列中是否有任务 没有的话 去别的线程窃取一个

package day04_5;

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

// Executor
/* ExecutorService
        submit
        shutDown
        isTerminated
    executors 工具类
        newFixedThreadPool()
        newCacheThreadPool()
        newScheduledThreadPool()// 周期执行
* */
public class TestExecutors {
    public static void main(String[] args) {
        //创建线程池
//        ExecutorService es = Executors.newFixedThreadPool(4);
        ExecutorService es = Executors.newCachedThreadPool();
        ExecutorService es = Executors.newWorkStealingPool();
        
// 创建任务
        Runnable ticket = new Runnable() {
            private int ticket = 100;

            @Override
            public void run() {
                while (true) {
                    if (ticket <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "票");
                    ticket--;
                }
            }
        };
        // 提交
        for (int i = 0; i < 4; i++) {
            es.submit(ticket);
        }
        es.shutdown();
    }
}

        线程池的七个参数

                ThreadPoolExecutor类的参数

                        corePoolSize: 核心线程数 maximumPoolSize :最大线程数

                        keepAliveTime 非核心线程的存活时间 unit :时间单位 

                        workQueue: 工作队列 threadFactory 线程工厂

                        handler 拒绝策略 (AboryPolicy 中断 DiscardPolicy直接抛弃 DiscardOldestPolicy                         把旧的抛弃  CallerRunsPolicy 线程池创建者执行 )       

package day04_5;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/*
* 七个参数
* 1.核心线程数
* 2.最大线程数
* 3.非核心线程存过时间
* 4.时间单位
* 5.工作队列
* 6.线程工厂
* 7.拒绝策略
* */
public class TestThreadPoolExecutor {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1,
                3,
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        // 创建任务并提交
        for (int i = 0; i < 4; i++) {
            int count = i;
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "开始执行" + count);
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "执行完毕");
                }
            });
        }
        executor.shutdown();

    }
}

十二、Callable 接口

        有返回值 可以抛出异常

        需要将Callable 转换珵任务 然后交给Thread  通过FutureTask类

package day05;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 计算 1-100的和
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        System.out.println(Thread.currentThread().getName() + "计算结束");
        return sum;
    }
}



package day05;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestCallable {
    public static void main(String[] args) {
        MyCallable mc = new MyCallable();
        // 需要将Callable 转换成任务
        FutureTask task = new FutureTask(mc);
        Thread th = new Thread(task);
        th.start();
        // 获取结果
        Integer res = null;
        try {
            res = (Integer) task.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        System.out.println("结果:" + res);
    }
}

(Callable接口实现多线程看上去是有点麻烦 不过好消息是 这个方法不会经常使用!!!)

十三、Callable接口更多的是和线程池配合使用

        Future 代表人物将要执行完毕的结果

        概念 异步接收ExecutorService.submit()所返回的状态结果,当中包含了call的返回值

package day05;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestCallable2 {
    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(1);
        MyCallable mc = new MyCallable();
        Future<Integer> res = es.submit(mc);
        try {
            Integer i = res.get();
            System.out.println("结果是:" + i);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

    }
}

十四、Lock接口

        jdk1.5之前的syn效率低 从无锁升级到重量级锁,从用户态切换到内核态

        jdk1.6syn优化为四种锁 无锁 偏向锁 轻量级锁,重量级锁

        常用方法 

                void lock() //获取锁 

                boolean tryLock()// 尝试获取锁  不阻塞

                void unlock()

        ReenTranLock可重入锁

package day05;

import java.util.concurrent.locks.ReentrantLock;

public class TestReentranLock {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            private Integer ticket = 100;
            private ReentrantLock lock = new ReentrantLock();
            @Override
            public void run() {
                while (true) {
                    lock.lock();
                    try {
                        if (ticket < 0) {
                            break;
                        }
                        System.out.println(Thread.currentThread().getName() + "卖了第" + ticket);
                        ticket--;
                    } finally {
                        lock.unlock();
                    }
                }
            }
        };

    }
}

通过可重入锁实现存取钱

package day05;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

public class TestReenTranLock3 {
    public static void main(String[] args) {
        Runnable take = new Runnable() {
            private Bankcard bankCard = new Bankcard();
            private ReentrantLock lock = new ReentrantLock();

            @Override
            public void run() {
                System.out.println(bankCard.getMoney() );
                for (int i = 0; i < 10; i++) {
                    lock.lock();
                    try {
                        System.out.println(Thread.currentThread().getName() + "存了1000余额是" + bankCard.getMoney());
                        bankCard.setMoney(bankCard.getMoney() + 1000);
                    } finally {
                        lock.unlock();
                    }
                }
            }
        };
        Runnable save = new Runnable() {
            private Bankcard bankCard = new Bankcard();
            private ReentrantLock lock = new ReentrantLock();

            @Override
            public void run() {
                System.out.println(bankCard.getMoney() );
                for (int i = 0; i < 10; i++) {
                    lock.lock();
                    try {
                        if (bankCard.getMoney() < 1000) {
                            System.out.println("余额不足");
                            i--;
                        }
                        bankCard.setMoney(bankCard.getMoney() - 1000);
                        System.out.println(Thread.currentThread().getName() + "取了1000余额是" + bankCard.getMoney());
                    } finally {
                        lock.unlock();
                    }
                }
            }
        };

        // 线程池创建两个线程
        ExecutorService es = Executors.newFixedThreadPool(2);
        es.submit(save);
        es.submit(take);

    }
}

读写锁-- ReentrantReadWriteLock

        一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁

package day05;

import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class readWrite {
    private Integer number = 0;
    //    private ReentrantLock lock = new ReentrantLock(); // 可重入锁
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    Runnable read = new Runnable() {
        @Override
        public void run() {
            // 18个线程读取
            rw.readLock().lock();
//            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 读取到的数 " + number);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                rw.readLock().unlock();
            }

        }
    };

    Runnable wrire = new Runnable() {

        @Override
        public void run() {
            // 2个线程写入
            rw.writeLock().lock();
//            lock.lock();
            try {
                number = new Random().nextInt(100);
                System.out.println(Thread.currentThread().getName() + " 写入到的数 " + number);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                rw.writeLock().unlock();
//                lock.unlock();
            }

        }
    };
}


package day05;

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

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

        readWrite rw = new readWrite();
        ExecutorService es = Executors.newFixedThreadPool(20);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 18; i++) {
            es.submit(rw.read);
        }
        for (int i = 0; i < 2; i++) {
            es.submit(rw.wrire);
        }
        long end = System.currentTimeMillis();
        es.shutdown();
        while (!es.isTerminated()) {
        }

        System.out.println("reentlock 用时 " + (end - start));

    }
}

十五、Condition-条件队列        

        Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待\通知模式

        Condition 可以通俗的理解为条件队列,当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。

        await —》当前线程进入等待状态

        signal---->唤醒一个等待线程

        使用Lock和Condition实现生产者和消费者

package day05_1;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Container {
    private Bread[] breads = new Bread[6];
    private int size;
    private ReentrantLock lock = new ReentrantLock();
    // 创建条件队列
    // condition实例必须绑定在锁上
    private Condition produceCondition = lock.newCondition();
    private Condition consumerCondition = lock.newCondition();

    public void input(Bread b) {
        lock.lock();
        try {
            if (size >= breads.length) {
                try {
                    produceCondition.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            breads[size] = b;
            size++;
            System.out.println(Thread.currentThread().getName() + "生产了" + b.getId());
            consumerCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    public void output() {
        lock.lock();
        try {
            if (size <= 0) {
                try {
                    consumerCondition.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            size--;
            Bread b = breads[size];
            breads[size] = null;
            System.out.println(Thread.currentThread().getName() + "消费了" + b.getId());
            produceCondition.signal();
        } finally {
            lock.unlock();
        }
    }
}

案例 交替输出ABC

package day05_1;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Example {
    private ReentrantLock lock = new ReentrantLock();
    private Condition a = lock.newCondition();
    private Condition b = lock.newCondition();
    private Condition c = lock.newCondition();
    private int num = 1;

    public void printA() {
        lock.lock();
        try {
            if (num != 1) {
                try {
                    a.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            } else {
                System.out.println("A");
                num = 2;
                b.signal();
            }

        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            if (num != 2) {
                try {
                    b.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            } else {
                System.out.println("B");
                num = 3;
                c.signal();

            }


        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            if (num != 3) {
                try {
                    c.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            } else {
                System.out.println("C");
                num = 1;

                a.signal();
            }
        } finally {
            lock.unlock();
        }
    }
}



package day05_1;

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

public class Test {
    public static void main(String[] args) {
        Example e = new Example();
        Runnable ra = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    e.printA();
                }
            }
        };
        Runnable rb = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    e.printB();
                }
            }
        };
        Runnable rc = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    e.printC();
                }
            }
        };
        ExecutorService es = Executors.newFixedThreadPool(3);
        es.submit(ra);
        es.submit(rb);
        es.submit(rc);
        es.shutdown();
    }
}

到这里结束!欢迎大家补充!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值