Java SE——高级学习第一讲

一、多线程

  • 多线程、进程与程序概念区别 程序:一段静态代码,静态对象,完成特定任务的一组指令的集合

  • 进程:(正在运行的程序)一个动态的过程,有它自身的产生存亡和消亡的过程——生命周期

  • 线程:将进程进行细化,是程序内部的一条执行路径;每个线程都有自己的程序计数器和虚拟运行栈,共用进程的方法区和堆空间。

  • 单核CPU和多核CPU理解:
    a.单核CPU:是一种假的多线程,因为在一个时间单元内也只能执行一个线程的任务,通过挂起和调用来实现假的多线程。多核CPU:更好的发挥多线程的效率。
    b.一个Java应用程序至少有:main主线程。gc垃圾回收线程,异常处理线程

  • 并行与并发:并行指多个CPU同时执行多个任务;并发指同一个CPU同时执行多个任务

二、线程的创建和使用

多线程的创建,方式一:继承于Thread类
 1、创建一个继承于Thread的子类
 2、重写Thread类的run()方法——>将此线程将执行的操作声明在run()中
 3、创建Thread类的子类对象
 4、通过此对象调用start()
package com.xyb.demo01;

/**
 * 多线程的创建,方式一:继承于Thread类
 * 1、创建一个继承于Thread的子类
 * 2、重写Thread类的run()方法——>将此线程将执行的操作声明在run()中
 * 3、创建Thread类的子类对象
 * 4、通过此对象调用start()
 * 例子:遍历100以内的所有偶数
 */

//1、创建一个继承于Thread类的子类
class MyThread extends Thread{
    //2、重写父类的run方法
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if (i%2==0)
                System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class MyThread2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i%2==1)
                System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class MultiThreading {
    public static void main(String[] args) {
        //3、创建Thread类的子类对象
        MyThread myThread=new MyThread();
        MyThread2 myThread2=new MyThread2();
        //4、通过此对象调用start():启动当前线程;调用当前线程的run()
        myThread.start();
        // 问题1:相当于直接调用run方法,还是在一个线程内,必须执行完run方法才能接着运行下面的程序
        //myThread.run();
        //问题2:再启动一个线程,不可以让已经启动的线程再次启动
        myThread2.start();
    }
}

Thread中的方法:

  • 测试Thread中的常用方法
  • 1、start():启动当前线程;调用当前的run方法
  • 2、run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
  • 3、currentThread():静态方法,返回执行当前代码的线程
  • 4、getName():获取当前线程的名字
  • 5、setName():设置当前线程的名字
  • 6、yield():释放当前CPU的执行权
  • 7、join():在线程A中调用线程B的join()方法,此时线程A就进入阻塞状态,直到B完全执行完再执行A
  • 8、stop():已经过时、当执行此方法时,强制停止线程
  • 9、sleep():让当前线程睡眠指定的毫秒,在这个时间内,当前线程是阻塞状态
  • 10、isAlive():判断当前线程是否存活
线程的优先级:
  • 1、
  •  MAX_PRIORITY:10
    
  •  MIN_PRIORITY:1
    
  •  NORM_PRIORITY:5
    
  • 2、如何获取和设置当前线程的优先级
  •  getPriority():获取当前优先级
    
  •  setPriority(int p):设置当前优先级
    
  •  说明:高优先级的线程会抢占低优先级的执行权,只是在高概率情况下会占用,并不说明一定会占用
    

*/

多线程的创建,方式二:实现Runnable接口

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

package com.xyb.demo01;

/**
 * 创建多线程的方式二:实现Runnable接口
 *  1、创建实现了Runnable接口的类
 *  2、实现类去实现Runnable中的抽象方法:run()
 *  3、创建实现类的对象
 *  4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
 *  5、通过Thread类的对象调用start()
 *
 *
 *  开发中:优先选择实现Runnable接口的方式
 *  原因:1、实现的方式没有类的单继承性的局限性
 *       2、实现的方式更适合来处理多个线程有共享数据的情况
 */

//1、创建实现了Runnable接口的类
class MThread implements Runnable{
    //2、实现类去实现Runnable中的抽象方法:run()
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if (i%2==0)
                System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class MultiThreading2 {
    public static void main(String[] args) {
        //3、创建实现类的对象
        MThread mThread = new MThread();
        //4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread thread = new Thread(mThread);
        //5、通过Thread类的对象调用start()
        thread.start();

    }
}
两种方式实现多窗口卖票的例子:

1、继承Thread类

package com.xyb.demo01;

/**
 * 例子:创建三个窗口卖票,总票数为100张(使用继承Thread类的方式)
 * 存在线程的安全问题,待解决
 */
class Windows extends Thread{
    private static int ticket=100;
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                System.out.println(getName()+":卖票,票号为"+ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}
public class WindowsTest {
    public static void main(String[] args) {
        Windows t1 = new Windows();
        Windows t2 = new Windows();
        Windows t3 = new Windows();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

2、实现Runnable接口

package com.xyb.demo01;

/**
 * 例子:创建三个窗口卖票,总票数为100张(使用实现Runnable接口的方式)
 */
class Windows1 implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while (true){
            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+":卖票,票号为"+ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}
public class WindowsTest2 {
    public static void main(String[] args) {
        Windows1 windows1=new Windows1();
        Thread t1=new Thread(windows1);
        Thread t2=new Thread(windows1);
        Thread t3=new Thread(windows1);
        t1.start();
        t2.start();
        t3.start();

    }
}

三、线程的生命周期

Thread类完整的生命周期包括五种状态:

  • 新建、就绪、运行、阻塞、死亡

四、线程的同步(解决线程安全的方法——3种)

  • 线程的死锁
    • 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源就形成了线程的死锁

    • 出现死锁后,不会出现异常,只是所有的线程都处于阻塞状态,无法继续

  • 解决方法
    • 专门的算法、原则
    • 尽量减少同步资源的定义
    • 尽量避免嵌套同步
package com.xyb.forth;

/**
 * 演示线程的死锁问题
 */
public class DeadLock {
    public static void main(String[] args) {
        StringBuffer s1=new StringBuffer();
        StringBuffer s2 =new StringBuffer();
        new Thread(){
            @Override
            public void run() {
                synchronized(s1){
                   s1.append("a");
                   s2.append("1");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                       s1.append("b");
                       s2.append("2");
                       System.out.println(s1);
                       System.out.println(s2);
                   }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(s2){
                    s1.append("c");
                    s2.append("3");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}

五、线程的通信

  • 线程通信的例子:使用两个线程打印1-100,线程1和线程2交替打印

  • wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器

  • notify():一旦执行此方法,就会唤醒wait的一个线程,如果有多个线程被wait,就唤醒优先级高的

  • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程

  • 面试题:sleep()和wait()的异同?

     1、相同点:一旦执行方法,都可以使当前的线程进入阻塞状态
     2、不同点:1):两个方法声明的位置不同,sleep在Thread中,wait在Object中
               2):调用的要求不同;sleep在任何需要的场景下调用,wait只能在同步代码块中
               3):sleep不会释放同步监视器,wait会释放同步监视器
    
package com.xyb.fifth;

/**
 * 线程通信的例子:使用两个线程打印1-100,线程1和线程2交替打印
 * wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器
 * notify():一旦执行此方法,就会唤醒wait的一个线程,如果有多个线程被wait,就唤醒优先级高的
 * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
 * 面试题:sleep()和wait()的异同?
 * 1、相同点:一旦执行方法,都可以使当前的线程进入阻塞状态
 * 2、不同点:1):两个方法声明的位置不同,sleep在Thread中,wait在Object中
 *          2):调用的要求不同;sleep在任何需要的场景下调用,wait只能在同步代码块中
 *          3):sleep不会释放同步监视器,wait会释放同步监视器
 */
public class ThreadCommunication {
    public static void main(String[] args) {
        Number number = new Number();
        Thread thread1 = new Thread(number);
        thread1.setName("线程1");
        Thread thread2 = new Thread(number);
        thread2.setName("线程2");
        thread1.start();
        thread2.start();
    }

}

class Number implements Runnable {
    private int number = 1;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                notify();
                //notifyAll();
                if (number <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}

六、JDK5.0新增线程创建方法(四种,5.0增加了2种)

新增方式一:实现Callable接口

  • 与Runnable相比,Callable功能更强大些
  • 相比run()方法,可以有返回值
  • 方法可以跑出异常
  • 支持泛型的返回值
  • 需要借助FutureTask类,比如获取返回结果
package com.xyb.sixth;

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

/**
 * 创建线程的方式三:实现Callable接口——JDK5.0新增
 * call()可以有返回值、可以跑出异常、支持泛型
 *
 */
//1、创建一个实现Callable的实现类
class NumberCallable implements Callable {
    //2、实现call方法,将此线程需要执行的操作声明放在call中
    @Override
    public Object call() {
        int sum=0;
        for (int i = 1; i <=100 ; i++) {
            if(i%2==0){
                System.out.println(i);
                sum+=i;
            }
        }
        return sum;
    }
}
public class ThreadCallable{
    public static void main(String[] args) {
        //3、创建Callable接口实现类的对象
        NumberCallable numberCallable=new NumberCallable();
        //4、将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numberCallable);
        //5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start
        new Thread(futureTask).start();
        try {
            //get()返回值为FutureTask构造器参数Callable实现类重写的call()的返回值
            Object sum = futureTask.get();
            System.out.println("总和为:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

新增方式二:使用线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完之后放回池中。可以避免频繁创建、实现重复利用。
好处:提高响应速度、降低资源消耗、便于线程管理

 - corePoolSize:核心池的大小
 - maximumPoolSize:最大线程数
 - keepAliveTime:线程没有任务时最多保持多长时间后会中止
package com.xyb.sixth;

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

/**
 * 创建线程的方式四:使用线程池
 * 使用线程池,线程池相关API:ExecutorService(真正的线程池接口)和Executors(工具类、线程池的工厂类,用于创建并返回不同类型的线程池)
 */

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);
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <=100 ; i++) {
            if(i%2!=0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //1、提供指定线程数量的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //设置线程池的属性
        ThreadPoolExecutor service=(ThreadPoolExecutor) executorService;
        service.setCorePoolSize(15);
        //2、执行指定的线程的操作。需要提供实现Runnable接口或者Callable接口实现类的对象
        executorService.execute(new NumberThread());//适合使用于Runnable
        executorService.execute(new NumberThread1());//适合使用于Runnable
        //executorService.submit();//适合使用与Callable
        //关闭连接池
        executorService.shutdown();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值