内部类创建多线程
- // 匿名内部类创建多线程
- public class InnerClassThread{
- public static void main(String[] args){
- // 回忆一下之前创建多线程的过程
- // 先创建一个 Thread 的子类, 通过这个子类, 重写 run 方法
- // 创建子类的对象. 通过子类对象调用 start(); 方法开启多线程
- // 现在试一下匿名内部类创建多线程
- new Thread(){
- public void run(){
- for (int i=0;i<10;i++){
- System.out.println(Thread.currentThread().getName()+"---"+i);
- }
- }
- }.start();
- // 之前通过 Runnable 开启多线程的步骤
- // 创建一个 Runnable 的实现类, 创建这个实现类的对象
- // 通过线程的构造方法传递实现类对象, 开启线程
- Runnable r = new Runnable(){
- public void run(){
- for (int i=0;i<10;i++){
- System.out.println(Thread.currentThread().getName()+"---"+"程序员");
- }
- }
- };
- new Thread(r).start();
- }
- }
多线程的安全问题
- // 实现 Implements 接口, 是开启多线程的前提
- public class TicketDemo implements Runnable {
- int ticket = 100;
- public void run() {
- while(true){
- if(ticket>0){
- try{
- Thread.sleep(10);
- }catch(Exception e){
- e.printStackTrace();
- }
- // 需要看一看是那一条线程抢到 CPU 的执行权
- System.out.println("我是"+Thread.currentThread().getName()+"正在出售第"+ticket+"票");
- ticket--;
- }
- }
- }
- }
- public class TicketObject{
- public static void main(String[] args){
- TicketDemo td = new TicketDemo();
- // 传递实现 implement 接口的对象
- Thread t1 = new Thread(td);
- Thread t2 = new Thread(td);
- Thread t3 = new Thread(td);
- t1.setName("吕布");
- t2.setName("典韦");
- t3.setName("关羽");
- // 开线程, 让他们互相抢夺 CPU
- t1.start();t2.start();t3.start();
- }
- }
这里出现了 0,-1 的票, 出现了线程安全问题
线程安全问题的解决方法_同步代码块
格式:
? synchronized(锁对象){
共享数据的代码
}
注意事项:
通过代码块中的所对象, 可以使用任意的对象
但是必须保证多个线程使用同一个对象
锁对象的作用是把同步代码块锁住, 只让一个线程在同步代码块中执行
- // 就在共享数据里加一个 synchronized 同步代码块就可以解决多线程的安全问题了
- while(true){
- synchronized(obj){
- if(ticket>0){
- try{
- Thread.sleep(10);
- }catch(Exception e){
- e.printStackTrace();
- }
- // 需要看一看是那一条线程抢到 CPU 的执行权
- System.out.println("我是"+Thread.currentThread().getName()+"正在出售第"+ticket+"票");
- ticket--;
- }
- }
- }
同步代码块的原理
使用了同一个锁对象, 这个锁对象叫做同步锁,
3 个线程一起抢夺 CPU 的执行权, 谁抢到谁执行 run 方法进行卖票
吕布抢到了 CPU 的执行权就会执行 run 方法, 遇到 synchronized 代码块, 这个时候吕布会检查 synchronized 代码块是否有锁对象, 有就会进入到同步中执行, 如果没有就等待, 不进入同步
当执行完了同步代码块的程序就会归还锁对象,
总结: 同步中的线程, 没有执行完毕不会释放锁, 同步外的线程没有锁进不去同步
并发与并行
并发: 是指 CPU 在同一个时间段内执行多个任务, 交替执行任务
并行: 是指 cup 在同一个时刻执行多个任务, 一下执行多个任务
解决线程安全问题_Lock 锁
使用步骤:
在成员位置创建一个 ReentrantLock 对象
在可能会出现安全问题的代码钱调用 Lock 接口中的方法 lock 获取锁
在可能出现安全问题的代码后调用 lock 接口中的方法 unlock 释放锁
- Lock l = new ReentrantLock();
- public void run() {
- while(true){
- l.lock();
- if(ticket>0){
- try{
- Thread.sleep(10);
- // 需要看一看是那一条线程抢到 CPU 的执行权
- System.out.println("我是"+Thread.currentThread().getName()+"正在出售第"+ticket+"票");
- ticket--;
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- l.unlock();
- }
线程的等待唤醒机制
什么是等待机制?
一个线程进行了规定操作后, 就进入等待状态 wait, 等待其他线程执行完他们的指定代码过后, 再将其唤醒 notify,
wait: 线程不在活动, 不在参与调度
notify: 线程会唤醒正在 wait 的线程
注意事项
wait 方法与 notify 方法必须要由同一个锁对象调用, 因为: 对应的锁对象可以通过 notify 唤醒使用同一个锁对象调用的 wait 方法后的线程
wait 方法与 notify 方法属于 Object 类的方法
wait 方法与 notify 方法必须要做同步代码块或者同步函数中使用
- public class BaoZi {
- // 包子是包子铺跟吃货的共享资源
- String pi;
- String xian;
- boolean flag = false;
- }
- // 包子铺是一个线程类, 生产包子, 但是包子不能够生产太快 (就是不可以让 CPU 全部被他占用了),
- // 包子过多, 不会吃不完, 所以需要一个等待与唤醒机制
- public class BaoZipu extends Thread{
- private BaoZi bz;
- public BaoZipu(BaoZi bz) {
- this.bz = bz;
- }
- public void run() {
- // 重写 run 方法, 设置线程任务, 任务是生产包子
- int count = 0;
- while (true) {
- synchronized (bz) {
- // 如果有包子, 就等待, 不需要生产包子
- if (bz.flag == true) {
- try {
- bz.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- // 没有包子, 就生产包子
- if (count % 2 == 0) {
- bz.pi = "剥皮";
- bz.xian = "三鲜馅";
- } else {
- bz.pi = "冰皮";
- bz.xian = "牛肉大葱馅";
- }
- count++;
- System.out.println("包子铺正在生产:" + bz.pi + bz.xian + "包子");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- // 包子有了有就改为 true 状态
- bz.flag = true;
- // 唤醒另外一条线程
- bz.notify();
- System.out.println("包子铺已经生产好了:" + bz.pi + bz.xian + "包子");
- }
- }
- }
- }
- public class ChiHuo extends Thread{
- private BaoZi bz;
- public ChiHuo(BaoZi bz){
- this.bz = bz;
- }
- public void run(){
- // 重写 run 方法, 设置线程任务, 任务是吃包子
- while(true){
- synchronized (bz){
- // 没有包子, 就等待包子的产生
- if(bz.flag==false){
- try {
- bz.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- // 有包子就吃包子
- System.out.println("吃货正在吃:"+bz.pi+bz.xian+"包子");
- // 吃完包子就把 flag 设置为 false
- bz.flag=false;
- // 唤醒线程另外一条线程
- bz.notify();
- System.out.println("吃货已经把:"+bz.pi+bz.xian+"的包子吃完了");
- System.out.println("-------------");
- }
- }
- }
- }
- public class Demo {
- public static void main(String[] args) {
- BaoZi bz = new BaoZi();
- // 开启线程, 执行 run 方法
- new BaoZipu(bz).start();
- new ChiHuo(bz).start();
- }
- }
进入 TimeWaitting(等待计时) 的两种方式
使用 sleep(long m) 方法, 在毫秒值结束之后, 线程睡醒进入到 runnable/bloked 状态
使用 wait(long m) 方法, wait 方法如果在毫秒值结束之后, 还没有被 notify 唤醒, 就会自动进入 Runnable/Blocked 状态
线程池
线程池就是一个容器, 可以使用集合来创建 (ArrayList,HashSet,LinkedList,HashMap)
当我们需要使用线程时可以从线程池中通过 remove 等方法取出来
取出来后还可以返回去, 通过 add 方法
但是在 JDK1.5 之后提供了一个创建线程池的工厂类, 用来生成线程池
java.util.concurrent.Executors: 线程池的工厂类
Executors 类中的静态方法: static ExcecutorService newFixedThreadPool(int nThreads) 创建一个可以重用固定线程数的线程池
参数: int nThreads: 创建线程池中包含的线程数量
返回值: ExecutorService 接口, 返回的就是 ExecutorService 接口的实现类对象, 我们可以使用 ExecutorService 接口接收
线程池的使用步骤:
使用线程池的工厂类 Executors 里边提供的静态方法 newFixedThreadPool 生产一个指定线程数量的线程池
创建一个类, 实现 Runnable 接口
调用 ExecutorService 中的方法 submit, 传递线程任务
调用 ExecutorService 中的方法 shutdown 销毁线程池
线程池的代码实现,
- // 创建 Runnable 接口实现类, 因为等下一下的 submit(); 方法会使用到它
- public class RunnableRun implements Runnable{
- public void run(){
- System.out.println(Thread.currentThread().getName()+"");
- }
- }
- import java.util.concurrent.*;
- public class ThreadPool{
- public static void main(String[] args){
- // 创建线程池工厂, 通过类 Executors 的方法 newFixedThreadPool();
- // 该类返回一个 ExecutorService 接口的实现类对象
- ExecutorService es = Executors.newFixedThreadPool(2);
- // 使用 ExecutorService 类的方法 submit() 来开启线程
- //submit 类需要传递一个 Runnable 接口的实现类对象
- // 返回的结果是线程执行的任务
- es.submit(new RunnableRun());
- es.submit(new RunnableRun());
- es.submit(new RunnableRun());
- es.submit(new RunnableRun());
- }
- }
Lambda 表达式
面向对象的思想:
? 做一件事情, 找一个能解决这个事情的对象, 调用对象的方法, 完成事情
函数式编程思想:
只要能获取到结果, 谁去做的, 怎么做的都不重要, 重视的是结果, 不重视过程
体验 Lambda 的更优写法
- public class LambdaDemo{
- public static void main(String[] args){
- // 匿名内部内创建多线程
- new Thread(new Runnable(){
- public void run(){
- System.out.println(Thread.currentThread().getName()+"线程名称");
- }
- }).start();
- //Lambda 表达式创建多线程
- new Thread(()->{
- System.out.println(Thread.currentThread().getName()+"线程名称");
- }
- ).start();
- }
- }
Lambda 标准格式
Lambda 表达式的标准格式:
由三部分组成:
? a. 一些参数 b. 一个箭头 c. 一段代码
格式:(参数列表) -> {一些重写的代码}
解析:
(); 接口中抽象方法的参数列表, 没有参数, 就空着, 由参数就写参数
-> : 传递的意思, 把参数传递给方法体 {}
(); 重写接口中的抽象方法的方法体
剩下几个 Lambda 表达式 f 的练习明天更......
来源: http://www.bubuko.com/infodetail-3070855.html