Java高级编程--多线程--day01

问题和知识点

        • 1.程序、进程、线程的基本概念是什么?
        • 2.并行与并发的区别?
        • 3.一个Java应用程序java.exe至少启动几个线程?
        • 4.多线程程序的优点:
        • 5.何时需要多线程
        • 6.Java中多线程实现方式
        • 7.Thread类中的有关方法
        • 8.线程的调度策略有哪些?
        • 9.java中线程的调度策略
        • 10.java中如何设置线程的优先级?
        • 11. 开发中:优先选择:实现Runnable接口的方式
        • 12.Java中的线程分为哪几类?
        • 13.线程的生命周期
        • 14.线程安全问题
        • 15.线程同步机制的方式有哪些?
        • 16.线程的死锁问题
        • 17.synchronized与Lock的异同?
        • 18.样例
        • 19.例题
        • 20.sleep()和wait()的异同?
        • 21.生产者/消费者问题

1.程序、进程、线程的基本概念是什么?

1.程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
2.进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
3.线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
1).若一个进程同一时间并行执行多个线程,就是支持多线程的
2).线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
3).一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

2.并行与并发的区别?

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

3.一个Java应用程序java.exe至少启动几个线程?

一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

4.多线程程序的优点:

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

5.何时需要多线程

  1. 程序需要同时执行两个或多个任务。
    2.程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
    3.需要一些后台运行的程序时。

6.Java中多线程实现方式

1.继承于Thread类
步骤:
1).创建一个继承于Thread类的子类
2).重写Thread类的run()
3).创建Thread类的子类的对象
4).通过此对象调用start()
例子:

public class ThreadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        for(int i=0;i<100;i++){
            if(i%2!=0){
                System.out.println(i);
            }
        }
        System.out.println("hello");
    }
}
class MyThread extends Thread{
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                System.out.println(i);
            }
        }
    }
}

注意:
1).start方法的作用:启动当前线程、调用当前线程的run方法,如果代码试图不经过start来运行线程,将导致线程不启动,只是线性的执行程序
2).获取当前线程名称:Thread.currentThread().getName()
3).不可以让已经start()的线程再去执行start(),会报IllegalThreadStateException异常。
4).创建自定义匿名线程

public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        new Thread(){
            public void run(){
                for(int i=0;i<100;i++){
                    if(i%2!=0){
                        System.out.println(i);
                    }
                }
            }
 }

2.通过实现Runnable接口的方式实现
1)创建一个实现了Runnable接口的类
2)实现类去实现Runnale中的抽象方法run()
3)创建实现类的对象
4)将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5)通过Thread类的对象调用start()

public class ThreadTest1 {
    public static void main(String[] args) {
        MyThread2 t1 = new MyThread2();
        for(int i=0;i<10;i++){
            new Thread(t1).start();
        }
    }
}
class MyThread2 implements Runnable{
    int temp=500;
    int test=0;
    @Override
    public void run() {
        while(temp>0)
        {
            System.out.println(temp--+"********************************");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.实现Callable接口(JDK5.0 新增线程创建方式)
与使用Runnable相比, Callable功能更强大些
1)相比run()方法,可以有返回值
2)方法可以抛出异常
3)支持泛型的返回值
4)需要借助FutureTask类,比如获取返回结果
创建步骤:
1)创建一个实现Callable的实现类,实现call方法
2)创建步骤1)创建的实例
3)将Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
4)将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
5)如果对返回值感兴趣,可以调用FutureTask的对象的get方法获取返回值。

class NumThread implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 1; i <=100; i++) {
            if(i%2==0){
                System.out.println(i);
                sum+=i;
            }
        }
        return sum;
    }
}
public class ThreadNew {
    public static void main(String[] args) {
        NumThread numThread=new NumThread();
        FutureTask<Integer> futureTask=new FutureTask<Integer>(numThread);
        Thread thread=new Thread(futureTask);
        thread.start();
        try {
            System.out.println("总和:"+futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

4.使用线程池(JDK5.0 新增线程创建方式)
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
 1)提高响应速度(减少了创建新线程的时间)
 2)降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 3)便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
 keepAliveTime:线程没有任务时最多保持多长时间后会终止
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
1)Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
 Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
 Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
 Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
 Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
使用步骤:
1)提供指定线程数量的线程池
2)执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
3)关闭线程池

public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService service= Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1=(ThreadPoolExecutor)service;
        service1.setCorePoolSize(100);
        service.execute(new NumberThread());//适用于Runnable
        service.submit(new NumThread());//适用于Callable
        service.shutdown();
    }
}
class NumberThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}

7.Thread类中的有关方法

Thread():创建新的Thread对象
Thread(String threadname):创建线程并指定线程实例名
Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
Thread(Runnable target, String name):创建新的Thread对象
1.void start(): 启动线程,并执行对象的run()方法
2.run(): 线程在被调度时执行的操作,该方法不能抛异常,只能用try-catch进行处理,因为父类没有抛出异常
3.String getName(): 返回线程的名称
4.void setName(String name):设置该线程名称
5.static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
6.yield():释放当前CPU执行权
7.join():当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join线程执行完为止低优先级的线程也可以获得执行
8.stop(): 强制线程生命期结束,不推荐使用
9.static void sleep(long millis):(指定时间:毫秒)
令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队
10.boolean isAlive():返回boolean,判断线程是否还活着

 public class ThreadTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        thread.setName("name2");
        for(int i=0;i<100;i++){
            if(i%2!=0) {
                System.out.println(Thread.currentThread().getName() + i);
            }
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.getMessage();
            }
        }
    }
}
class MyThread extends Thread{
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+i);
            }
            if(i%20==0){
                yield();
            }
        }
    }
}

8.线程的调度策略有哪些?

1.时间片
2.抢占式

9.java中线程的调度策略

1.同优先级线程组成先进先出队列(先到先服务),使用时间片策略
2.对高优先级,使用优先调度的抢占式策略

10.java中如何设置线程的优先级?

Thread中定义的两个方法

public final int getPriority();
public final void setPriority(int newPriority);

getPriority():返回线程优先值
setPriority(int newPriority):改变线程的优先级别
线程的优先等级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5//默认优先级

11. 开发中:优先选择:实现Runnable接口的方式

原因:1. 实现的方式没有类的单继承性的局限性
2. 实现的方式更适合来处理多个线程有共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

12.Java中的线程分为哪几类?

一种是守护线程:Java垃圾回收就是一个典型的守护线程。守护线程是用来服务用户线程的,通过在start()方法前调用
thread.setDaemon(true)可以把一个用户线程变成一个守护线程。若JVM中都是守护线程,当前JVM将退出。
一种是用户线程:它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。

13.线程的生命周期

JDK中用Thread.State类定义了线程的6种状态
Java高级编程--多线程--day01_第1张图片
在它的一个完整的生命周期中通常要经历如下的五种状态:
Java高级编程--多线程--day01_第2张图片

14.线程安全问题

多个线程执行的不确定性引起执行结果的不稳定,多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
可以通过线程同步机制来解决线程安全问题。
方式一、同步代码块
方式二、同步方法
方式三、Lock锁,JDK5.0之后

15.线程同步机制的方式有哪些?

  1. 同步代码块:
    synchronized (对象){
    // 需要被同步的代码;操作共享数据的代码
    }
    说明:
    任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
    必须确保使用同一个资源的多个线程共用一把锁,
    解决了线程安全的问题,但是效率变低了
public class ThreadTest1 {
    public static void main(String[] args) {
        MyThread2 t1 = new MyThread2();
        for(int i=0;i<10;i++){
            new Thread(t1).start();
        }
    }
}
class MyThread2 implements Runnable{
    int temp=500;
    int test=0;
    Object obj=new Object();
    @Override
    public void run() {
        while(temp>0)
        {
            synchronized(MyThread2.class){System.out.println(temp--+"********************************");}
            //synchronized(obj){System.out.println(temp--+"********************************");}
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. synchronized还可以放在方法声明中,表示整个方法为同步方法。
    例如:
    public synchronized void show (String name){
    ….
    }
    样例1://同步监视器this
public class ThreadTest1 {
    public static void main(String[] args) {
        MyThread2 t1 = new MyThread2();
        for(int i=0;i<10;i++){
            new Thread(t1).start();
        }
    }
}
class MyThread2 implements Runnable{
    int temp=500;
    int test=0;
    Object obj=new Object();
    @Override
    public void run() {
        while(temp>0)
        {
           show();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized void show(){
        System.out.println(temp--+"********************************");
    }
}

样例2://同步监视器使用static为类

public class ThreadTest {
    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            new MyThread().start();
        }
    }
}
class MyThread extends Thread{
    static int x=500;
    public MyThread() {
    }
    public MyThread(String name) {
        super(name);
    }
    public void run(){
        while(x>0)
        {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           show();
        }
    }
    private static synchronized void show(){
        System.out.println(x--+"********************************");
    }
}

线程安全的懒汉式:

class Single{
    private static Single single=null;
    private Single(){
    }
    public static Single getSingleInstance()  {
        if(single==null){
            synchronized (Single.class){
                if(single==null){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    single=new Single();
                }
            }
        }
        return single;
    }
}

3.Lock锁,JDK5.0之后
样例代码:

public class LockTest {
    public static void main(String[] args) {
        Window window = new Window();
        new Thread(window).start();
        new Thread(window).start();
        new Thread(window).start();
        new Thread(window).start();
    }
}
class Window implements Runnable{
    private int ticket=100;
    private ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        while(true){
            lock.lock();
            if(ticket>0){
                try {
                    System.out.println(Thread.currentThread().getName()+":售票,票号为"+ticket--);
                }finally {
                    lock.unlock();
                }
            }else{
                break;
            }
        }
    }
}

16.线程的死锁问题

1).不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2).出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
1.解决方法
1)专门的算法、原则
2)尽量减少同步资源的定义
3)尽量避免嵌套同步

17.synchronized与Lock的异同?

相同:两者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unLock())

18.样例

public class AccountTest {
    public static void main(String[] args) {
        Account account=new Account(0);
        Customer c1=new Customer(account);
        c1.setMoney(1000);
        c1.start();
        Customer c2=new Customer(account);
        c2.setMoney(1000);
        c2.start();
    }
}
class Customer extends Thread{
    private Account account;
    private double money;
    public Customer(Account account) {
        this.account = account;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    public void run(){
        for(int i=0;i<3;i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (Customer.class){
                account.withdrow(money);
                System.out.println(this.getName()+account.getBalance());
            }
        }
    }
}
class Account{
    private double balance;
    public double getBalance() {
        return balance;
    }
    public Account(double balance) {
        this.balance = balance;
    }
    public Account() {
    }
    public void withdrow(double balance){
        this.balance=this.balance+balance;
    }
}

19.例题

使用两个线程打印 1-100。线程1, 线程2 交替打印

public class Communication {
    public static void main(String[] args) {
        MyThreadCommunication myThreadCommunication=new MyThreadCommunication();
        Thread thread1=new Thread(myThreadCommunication);
        Thread thread2=new Thread(myThreadCommunication);
        thread1.setName("线程1");
        thread2.setName("线程2");
        thread1.start();
        thread2.start();
    }
}
class MyThreadCommunication implements Runnable {
    int i=1;
    public void run(){
        while(true){
            synchronized (this){
                notify();
                if(i<=100){
                    System.out.println(Thread.currentThread().getName()+":"+(i++));
                }else break;
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

涉及到的三个方法说明:
1.wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
2.notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级别高的线程
3.notifyAll():唤醒所有被wait的线程。
三个方法必须使用在同步代码块或同步方法中
三个方法的调用者必须是同步代码块或同步方法中的同步监视器
三个方法都是定义在java.lang.Object类中

20.sleep()和wait()的异同?

相同点:一旦执行方法,都可以使当前的线程进入阻塞
不同点:
1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait();
2)调用的要求不同:sleep()可以在任何需要的场景下使用,wait()必须使用在同步代码块中
3)关于是否释放同步监听器,如果两个方法都是用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

21.生产者/消费者问题

public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();
        Producer producer=new Producer(clerk);
        producer.setName("生产者");
        Customer2 customer=new Customer2(clerk);
        customer.setName("消费者");
        producer.start();
        customer.start();
    }
}
class Clerk{
    private int productCount=0;
    public synchronized void  produceProduct() {
        if(productCount<20){
            productCount++;
            System.out.println(Thread.currentThread().getName()+":开始生产"+productCount);
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized void comsumeProduct() {
        if(productCount>0)
        {
            System.out.println(Thread.currentThread().getName()+":开始消费"+productCount);
            productCount--;
            notify();
        }else{
		try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Producer extends Thread{
    private Clerk clerk;
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName()+":开始生产产品....");
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}
class Customer2 extends Thread{
    private Clerk clerk;
    public Customer2(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName()+":开始消费产品....");
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.comsumeProduct();
        }
    }
}

你可能感兴趣的:(Java高级编程--多线程--day01)