day19-线程

线程

t1 输出 ++++++++++++++
t2 输出 ———————–
t3 输出 ******

t1 obj.wait() flag==1
t2 obj.wait() flag ==2
t3 obj.wait() flag ==3

jdk 1.5 5.0

java.util.concurrent.* java并发工具包

1. 创建线程的第三种方式

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

与Runnable的区别,1)有返回结果,2)可以抛出检查异常

创建代码:

// 代表一个任务对象
// Callable代表线程中要执行的代码
FutureTask task = new FutureTask(new Callable() {
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.sleep(2000);
        return "ok";
    }
});

// 创建和启动新线程
new Thread(task).start();

// 获取返回结果
System.out.println(task.get());

2. 线程池

创建有限的线程资源为更多的任务提供服务。享元模式

// java中对线程池的抽象:ExecutorService  创建一个固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);

/*for (int i = 0; i < 10; i++) {
    threadPool.submit(()->{
        System.out.println(Thread.currentThread().getName()+"任务执行");
    });
}*/
// 执行带有返回结果的任务
Future future = threadPool.submit(() -> {
    System.out.println(Thread.currentThread().getName()+"执行计算...");
    Thread.sleep(1000);
    return 10;
});
System.out.println(future.get());


threadPool.shutdown(); // 不接收新任务,当所有任务运行结束,整个线程池关闭

一个核心的ExecutorService的实现类:ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue)

corePoolSize 核心线程数目 (最多保留的线程数)
5
maximumPoolSize
10

workQueue 阻塞队列 如果任务超过了核心线程数,进入队列进行排队,直到有空闲的线程
10

如果任务过多,阻塞队列都放不下了,还会创建新的线程来救急
corePoolSize+救急的线程 <= maximumPoolSize(最大线程数)
21 会抛出拒绝提交任务异常

keepAliveTime 生存时间- 针对救急线程
60
unit 时间单位 秒

// 创建固定大小的线程池
Executors.newFixedThreadPool(2);
核心线程数=最大线程数(没有救急线程被创建)
阻塞队列 无界,可以放任意数量的任务,
适合执行数量有限,长时间运行的任务

// 创建缓冲线程池
Executors.newCachedThreadPool()
核心线程数是0, 最大线程数是Integer的最大值(救急线程可以无限创建)
生存时间是60s
适合任务数比较密集,但每个任务执行时间较短的情况

// 创建单线程线程池
Executors.newSingleThreadExecutor()
使用场景:希望多个任务排队执行

区别:
Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改
Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改

// 带有日程安排功能的线程池

ScheduledExecutorService service = Executors.newScheduledThreadPool(5);

// 让任务推迟一段时间执行
// 参数1.任务对象, 参数2,3 推迟的时间
/*service.schedule(()->{
    System.out.println("执行任务...");
}, 10L, TimeUnit.SECONDS);*/

// 以一定的频率反复执行任务(任务不会重叠)
// 参数1,任务对象, 参数2,初始推迟时间, 参数3,4 时间间隔和单位
/*service.scheduleAtFixedRate(()->{
    try {
        Thread.sleep(1200);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);*/

// delay表示从上一个任务结束,到下一个任务开始之间的时间
service.scheduleWithFixedDelay(()->{
    System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);


//        service.shutdown();

3. 原子操作类

AtomicInteger
AtomicBoolean

// 创建原子整数类
private static AtomicInteger i = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            i.getAndIncrement();  // 获取并且自增  i++
//                i.incrementAndGet();  // 自增并且获取  ++i
        }
    });

    Thread t2 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            i.getAndDecrement(); // 获取并且自减  i--
        }
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(i);
}

4. 线程安全集合类

StringBuffer 线程安全
String 不可变类 , 都是线程安全的
Random 线程安全
Vector 实现了List,并且线程安全
Hashtable 实现了Map,并且线程安全

5.0新增的线程安全集合类
ConcurrentHashMap 实现了Map,并且线程安全
ConcurrentSkipListMap 实现了Map(可排序),并且线程安全
CopyOnWriteArrayList 实现了List,并且线程安全

BlockingQueue 阻塞队列
队列 FIFO , first in first out

Queue –> LinkedList

private static BlockingQueue queue = new ArrayBlockingQueue<>(5);
public static void main(String[] args) {

    // 生产者线程
    new Thread(()->{
        for (int i = 0; i < 10; i++) {
            Product p = new Product(i);
            System.out.println(Thread.currentThread().getName()+"生产了:"+p);
            try {
                queue.put(p);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();

    // 消费者线程
    for (int j = 0; j < 5; j++) {
        new Thread(()->{
            for (int i = 0; i < 2; i++) {
                try {
                    Product p = queue.take();
                    System.out.println(Thread.currentThread().getName()+"消费了:"+p);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }


}

5. ThreadLocal

//             线程局部变量
private static ThreadLocal local = new ThreadLocal() {
@Override        // 初始值
protected Object initialValue() {
    return new SimpleDateFormat("yyyy-MM-dd"); // 存入当前线程
}
};


public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
    new Thread(()->{
        try {
            SimpleDateFormat sdf = local.get(); // 获取本线程自己的局部变量
            Date date = sdf.parse("1951-10-09"); // 每个线程使用的是自己的SimpleDateFormat因此没有争用
            System.out.println(Thread.currentThread().getName() + " " + date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }).start();
}

/*for (int i = 0; i < 10; i++) {
    new Thread(()->{
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse("1951-10-09");
            System.out.println(Thread.currentThread().getName() + " " + date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }).start();
}*/

趁热打铁

@(西部开源)

线程习题

选择题

  1. 说法正确的是:
public class Example implements Runnable {
    public void run() {
        while (true) {
        }
    }

    public static void main(String args[]) {
        Example ex1 = new Example();
        Example ex2 = new Example();
        Example ex3 = new Example();
        ex1.run();
        ex2.run();
        ex3.run();
    }
}
A. 代码编译失败,不能直接调用run方法
B. 代码编译成功,存在3 个可运行的线程
C. 代码编译成功,存在1 个可运行的线程
  1. 说法正确的是
public class Example extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName());
        }

    }
    public static void main(String args[]){
        Thread t = new Example();
        t.start();
        t.start();
        t.start();
    }
}
A. 编译出错
B. 编译正常,运行时有错
C. 编译运行都无错误,产生1 个线程
D. 编译运行都无错误,产生3 个线程
  1. 说出运行结果,如果把world改为hello呢?
public class Example extends Thread {
    private String data;

    public Example(String data) {
        this.data = data;
    }

    public void run() {
        synchronized (data) {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep((int) (Math.random() * 1000));
                } catch (Exception e) {
                }
                System.out.println(Thread.currentThread().getName() + " " + data);
            }
        }
    }

    public static void main(String args[]) {
        Thread t1 = new Example("hello");
        Thread t2 = new Example("world");
        t1.start();
        t2.start();
    }
}
A. 先输出10 个hello,然后是10 个world
B. 先输出10 个world,然后是10 个hello
C. 线程不同步,因此交替输出hello 和world
  1. 接上题,如果不希望交替输出hello和world,有哪些解决办法?

  2. 线程八锁,分别说出下列八种情况的打印结果
    情况1:

class Number{
    public synchronized void a() {
        System.out.println(1);
    }
    public synchronized void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

情况2:

class Number{
    public synchronized void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

情况3:

class Number{
    public synchronized void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized void b() {
        System.out.println(2);
    }
    public void c() {
        System.out.println(3);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
    new Thread(()->{ n1.c(); }).start();
}

情况4:

class Number{
    public synchronized void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

情况5:

class Number{
    public synchronized static void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

情况6:

class Number{
    public synchronized static void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized static void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n1.b(); }).start();
}

情况7:

class Number{
    public synchronized static void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

情况8:

class Number{
    public synchronized static void a() {
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        System.out.println(1);
    }
    public synchronized static void b() {
        System.out.println(2);
    }
}
public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    new Thread(()->{ n1.a(); }).start();
    new Thread(()->{ n2.b(); }).start();
}

编程题

  1. 创建三个线程,要求如下:
    1)第一个线程输出一行100个*,使用继承Thread 类的写法
    2)第二个线程输出一行100个#,使用实现Runnable 接口的写法
    3)第三个线程输出一行100个+,用lambda表达式(本质仍是Runnable)的写法

  2. 接上题
    如果希望输出不产生交错,如何解决

  3. 接上题
    如果希望先输出+再输出*最后输出#,怎么解决

解答:

package org.westos.thread;

import org.junit.Test;

/**
 * *************************************
 * Copyright (c) 2018 feiyan.com
 * All rights reserved.
 * *************************************
 * *************************************
 *
 * @Author: think
 * @Project: JavaSE
 * @Date: Created in 2018/8/12 9:01
 * @Since: JDK 1.8
 * @Version: 1.0
 * @Modified By:
 * @Description:
 */
public class HomeworkDemo {

    public static final Object OBJ = new Object();

    public static void main(String[] args) {

        // test3();

        // test4();

        // test5();

        // test6();

        //test7();

        // test8();

        test9();
    }

    @Test
    public void test1() {
        class Example implements Runnable {
            @Override
            public void run() {
                while (true) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }


        Example ex1 = new Example();
        Example ex2 = new Example();
        Example ex3 = new Example();
        ex1.run();
        ex2.run();
        ex3.run();
    }

    @Test
    public void test2() {
        class Example extends Thread {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName());
                }

            }
        }

        Thread t = new Example();
        t.start();
//        t.start();
//        t.start();
    }


    public static void test3() {
        class Example extends Thread {

            private String data;

            public Example(String data) {
                this.data = data;
            }

            @Override
            public void run() {
                synchronized (OBJ) {
                    for (int i = 0; i < 10; i++) {
                        try {
                            Thread.sleep((int) (Math.random() * 1000));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        System.out.println(Thread.currentThread().getName() + " " + data);
                    }
                }
            }
        }


        Thread t1 = new Example("hello");
        Thread t2 = new Example("world");
        t1.start();
        t2.start();
    }


    public static void test4() {
        Number n1 = new Number();
        new Thread(() -> {
            n1.a();
        }).start();

        new Thread(() -> {
            n1.b();
        }).start();
    }

    public static void test5() {
        Number n1 = new Number();
        new Thread(() -> {
            n1.a();
        }).start();

        new Thread(() -> {
            n1.b();
        }).start();

        new Thread(() -> {
            n1.c();
        }).start();
    }

    public static void test6() {
        Number n1 = new Number();
        Number n2 = new Number();
        new Thread(() -> {
            n1.a();
        }).start();

        new Thread(() -> {
            n2.b();
        }).start();
    }

    public static void test7() {
        Number n1 = new Number();
        new Thread(() -> {
            n1.a();
        }).start();

        new Thread(() -> {
            n1.b();
        }).start();
    }

    public static void test8() {
        Number n1 = new Number();
        Number n2 = new Number();
        new Thread(() -> {
            n1.a();
        }).start();

        new Thread(() -> {
            n2.b();
        }).start();
    }


    public static void test9() {

        final Object obj = new Object();

        class T1 extends Thread {
            @Override
            public void run() {
                synchronized (obj) {
                    for (int i = 0; i < 100; i++) {
                        System.out.print("*");
                    }
                    System.out.println();
                }
            }
        }
        new T1().start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        obj.wait(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }


                    for (int i = 0; i < 100; i++) {
                        System.out.print("#");
                    }
                    System.out.println();
                }
            }
        }).start();

        new Thread(() -> {
            synchronized (obj) {
                try {
                    obj.wait(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                for (int i = 0; i < 100; i++) {
                    System.out.print("+");
                }
                System.out.println();
            }
        }).start();
    }
}


class Number {
    public synchronized static void a() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(1);
    }

    public synchronized void b() {
        System.out.println(2);
    }

    public void c() {
        System.out.println(3);
    }
}

简答题

  1. 多线程有什么用?
  2. 有哪几种创建线程的方式?
  3. start方法和run方法的区别
  4. Runnable与Callable的区别
  5. volatile关键字的使用
  6. CyclicBarrier和CountDownLatch的区别
  7. 你所知道的线程安全集合类有哪些?
  8. 如何在两个线程之间共享数据
  9. sleep和wait方法有什么区别
  10. 生产者消费者模型的作用?
  11. ThreadLocal有什么用
  12. 线程池的作用?高并发、时间短用哪种线程池,并发不高、执行时间长用哪种线程池?
  13. synchronized和ReentrantLock的区别
  14. 在什么情况下会造成死锁,如何检测
  15. 不可变对象的意义
  16. 什么是CAS
  17. 什么是悲观锁和乐观锁

你可能感兴趣的:(Java)