javaSE--多线程


typora-copy-images-to: …\qq截图

多线程

1.基本概念

程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象
进程:是程序的一次执行过程,或者是正在运行中的程序,是一个动态的过程:有它自身的产生,存在和消亡的过程---生命周期
	程序是静态的,进程是动态的,进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

线程:进程可以进一步细化为线程,是一个程序内部的一条执行路径,
	若一个程序同一时间并行执行多个线程,就是支持多线程的 比如360 里面的木马查杀运行的时候可以同时运行优化加速,也可以运行磁盘清理
	线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器。线程切换的开销小
	一个进程里面可能会有多个线程,一个进程中的多个线程共享相同的内存单元/内存地址空间,他们从同一堆中分配对象,可以访问相同的变量和对象,这就使得线程之间的通信更加简便,高效,但多个线程之间的共享的系统资源可能就会带来安全隐患
	举例:合租 你租一个房子,你朋友租一个房子,厨房和卫生间共用
	
一个java应用程序至少有三个线程,main线程--主线程  gc线程---垃圾回收线程  异常处理线程

并行:多个CPU同时执行多个任务,比如说多个人同时做多个事  并行:同时进行
并发:一个CPU(采用时间片)同时执行多个任务,多个人做同一件事 ,这里的同时其实时时间片快速的切换 并发:交替进行,只不过交替的时间非常短
多线程的优点:
1.提高应用程序的响应,对图形界面更有意义,可增强用户体验
2.提高计算机系统的CPU利用率
3.改善程序的结构,将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

2.线程的创建和使用

2.1 线程创建的第一种方式Thread

/**
 * 1.自定义一个类,继承thread类
 * 2.重写run方法
 * 3.调用start方法,开启线程
 */
public class ThreadTest {
    public static void main ( String[] args ) {
        MyThread mt = new MyThread();
        mt.start(); //开启一个线程,然后调用run方法,注意,已经开启的线程不能再调用start方法
        mt.run();  //如果这里调用的时run方法,相当于方法调用,调用了MyThread重写的run方法
    }
}
class MyThread extends Thread{
    @Override
    public void run () {
        for (int i = 0;i<100;i++){
            if (i%2==0){
                System.out.println(i);
            }
        }
    }
}
/**
 * 采用匿名内部类的方式开启线程
 */
public class ThreadTest02 {
    public static void main ( String[] args ) {
      
        new Thread(){
//            打印0-100的偶数
            @Override
            public void run () {
                for (int i = 0;i<100;i++){
                    if (i%2==0){
//                        Thread.currentThread().getName()获取当前线程的名称
                        System.out.println(Thread.currentThread().getName()+i);
                    }
                }
            }
        }.start();
       
        new Thread(){
//            打印0-100之间的奇数
            @Override
            public void run () {
                for (int i = 0;i<100;i++){
                    if (i%2!=0){
//                        Thread.currentThread().getName()获取当前线程的名称
                        System.out.println(Thread.currentThread().getName()+i);
                    }
                }
            }
        }.start();

        System.out.println(Thread.currentThread().getName()+"main线程");
    }
}

2.2 Thread类中的常用方法

1.public static Thread currentThread():返回正在运行的线程的对象的引用;
  他具有Thread类的其他成员方法 :
	Thread.currentThread().setName():设置主线程名称
2.start():启动当前线程,调用当前线程的run()方法
3.run():通常需要重写Thread类的此方法,将创建的线程需要执行的操作声明再此方法中
4.getName():获取当前线程的名称
5.setName():给当前线程设置名称
6.public final void join () throw InterruptedException(): 如果线程a调用线程b的join(),此时线程a就会进入阻塞状态,直到线程b执行结束
7.yield():释放当前线程的CPU执行权
8.stop():强制结束当前线程,已过期
9.sleep(long millitime):让当前线程睡眠指定的millitime毫秒,再指定的时间内,线程都处于阻塞状态
10.isAlive():判断当前线程是否存活
11.public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,
	该方法必须在启动线程前调用,形参如果为true。则将该线程标记为守护线程
	注意 :守护线程没有主线程无法运行, 主线程停止,守护线程也随之停止
	守护线程也和主线程抢占cpu资源
12.线程的优先级
	public final int getPriority() 获取线程的优先级
	public final void setPriority(int Priority):给当前线程设置一个优先级
	默认优先级:5
	Thread类中存在以下的优先级的常量
	public static final int MAX_PRIORITY 10  最大
 	public static final int MIN_PRIORITY 1   最小
 	public static final int NORM_PRIORITY 5   默认
class DeamoThread extends Thread{
    @Override
    public void run () {
        for (;;){
            System.out.println("我是"+getName());
        }
    }
}
public class ThreadTest03 {
    public static void main ( String[] args ) {
        DeamoThread deamoThread = new DeamoThread();
//      给线程设置一个名称
        deamoThread.setName("守护线程");
//       设置为守护线程
        deamoThread.setDaemon(true);
//        设置一个优先级
        deamoThread.setPriority(Thread.MAX_PRIORITY);
        deamoThread.start();
//       给主线程设置名称
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

2.3 多窗口售票的问题

class TicketThread extends Thread{
    private static int ticket = 100;

    @Override
    public void run () {
        while (true){
            if (ticket > 0){
                System.out.println(getName()+"正在售出第"+ticket+"张票");
                ticket--;
            }else{
                System.out.println("票已售完");
                break;
            }
        }
    }
}
public class ThreadTest04 {
    public static void main ( String[] args ) {
        TicketThread th1 = new TicketThread();
        TicketThread th2 = new TicketThread();
        TicketThread th3 = new TicketThread();
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}
//三个窗口再抢占CPU资源的时候,会出现线程安全问题后面会解决

2.4 线程创建的第二种方式Runnable

/**
 * 1.自定义一个类,实现Runnable接口
 * 2.重写run方法
 * 3.创建Thread类对象,将实现类的对象作为形参,传入Thread的构造方法中
 * 4.调用Thread类的start()方法,启动该线程
 */
class MyRunable implements Runnable{
    public void run () {
        System.out.println("nihao,Runnable");
    }
}
public class RunableTest01 {
    public static void main ( String[] args ) {
        MyRunable myRunable = new MyRunable();
        new Thread(myRunable).start();
    }
}

2.5 实现Runable接口的多窗口卖票

class Ticket implements  Runnable{
    private int ticket = 100;
    public void run () {
        while (true){
            if (ticket > 0){
                System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
                ticket--;
            }else{
                System.out.println("票已售完");
                break;
            }
        }
    }
}
public class TicketRunnable {
    public static void main ( String[] args ) {
//        实现Runnable接口的实现类的对象是一个共享对象
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

3.线程的生命周期

//Thread.State中定义了线程的五种状态
public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

3.1 线程的五种状态

新建:当一个Thread类或者子类的对象被声明创建时,新生的线程的线程处于新建状态
就绪:处于新建状态的线程被start()之后,将进入线程队列等待CPU时间片,此时它已经具备了运行的条件,只是没有分配到CPU资源
运行状态:当就绪的线程被调度,并分配到CPU资源的时候,便进入运行状态,run()方法定义了线程的操作和功能
阻塞:在某种特殊的情况下,被人挂起或执行输入输出操作时,让出CPU资源并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作,或者提前被强制性的中止或出现异常导致结束

3.2 线程的生命周期

在这里插入图片描述

4.线程的同步

问题:上述卖票中,出现了重票,错票的问题---也就是线程安全的问题
出现问题的原因:当一个线程操作车票的过程中,还没有操作完成,另一个线程也参与进来,也操作车票,这就导致了一些列问题
如何解决:当一个线程操作车票的时候,我们不让其他线程参与进来,直到线程完成车票的操作。即使这个时候线程出现了阻塞,也必须等待该线程结束
在java中,为了解决这种问题,提供了同步机制, synchronized

4.1 同步代码块

/**
 * 同步代码块
 * synchronized(同步监听器){
 *     需要同步的代码
 * }
 * 1.操作共享数据的代码,为被需要同步的代码
 * 2.共享数据,多个线程共同操作的变量
 * 3.同步监听器,俗称锁,任何一个类的对象,都可以充当锁
 *            注意:多个线程必须是同一把锁
 */
class Ticket implements  Runnable{
    private int ticket = 100;
    public void run () {
        while (true){
            synchronized (this){
                if (ticket > 0){
                    System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
                    ticket--;
                }else{
                    System.out.println("票已售完");
                    break;
                }
            }
        }
    }
}
public class TicketRunnable {
    public static void main ( String[] args ) {
//        实现Runnable接口的实现类的对象是一个共享对象
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

//如果是继承自Thread类
class TicketThread extends Thread{
    private static  Object object = new Object(); //让三个线程公用一个对象
    private static int ticket = 100;

    @Override
    public void run () {
        while (true){
            synchronized (object){
         //   synchronized (this){ //如果是继承自thread类,这里是this,显然是错误的,
                //因为this代表的是当前对象,每个线程都有自己的对象
                if (ticket > 0){
                    System.out.println(getName()+"正在售出第"+ticket+"张票");
                    ticket--;
                }else{
                    System.out.println("票已售完");
                    break;
                }
            }

        }
    }
}
public class ThreadTest04 {
    public static void main ( String[] args ) {
        TicketThread th1 = new TicketThread();
        TicketThread th2 = new TicketThread();
        TicketThread th3 = new TicketThread();
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

4.2 同步方法

/**
 * 同步方法:
 * 1.同步方法依然涉及到同步监听器,只是不需要显示的声明
 * 2.非静态的同步方法,同步监听器是this
 *  静态的同步方法,同步监听器是当前类本身,因为静态方法与类有关,优先于对象
 */
class TicketThread extends Thread{
    private static  Object object = new Object(); //让三个线程公用一个对象
    private static int ticket = 100;

    @Override
    public void run () {
        while (ticket>0){
            sellTicket();
        }
    }
    private synchronized static void sellTicket(){
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
            ticket--;
        }else{
            System.out.println("票已售完");
        }
    }
}
public class ThreadTest04 {
    public static void main ( String[] args ) {
        TicketThread th1 = new TicketThread();
        TicketThread th2 = new TicketThread();
        TicketThread th3 = new TicketThread();
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

//如果实现Runnable接口
class Ticket implements  Runnable{
    private int ticket = 100;
    public void run () {
        while (ticket>0){
            sellTicket();
        }
    }
    private synchronized void sellTicket(){
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
            ticket--;
        }else{
            System.out.println("票已售完");
        }
    }
}
public class TicketRunnable {
    public static void main ( String[] args ) {
//        实现Runnable接口的实现类的对象是一个共享对象
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

//注意:如果是继承Thread类继承多线程,那么同步方法的static一定要判断是否需要加上

4.3 Lock

JDK 5.0以后,java提供了更加强大的线程同步机制---通过显式定义同步锁对象来实现同步
class Ticket implements  Runnable{
    //创建一个lock对象,Lock是一个接口,需要它的子实现类
    private ReentrantLock lock = new ReentrantLock();
    private int ticket = 100;
    public void run () {
        while (true){
            try {
                lock.lock();
                if (ticket > 0){
                    System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
                    ticket--;
                }else{
                    System.out.println("票已售完");
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
public class TicketRunnable {
    public static void main ( String[] args ) {
//        实现Runnable接口的实现类的对象是一个共享对象
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

// 继承自Thread类
class TicketThread extends Thread{
//    这里必须有static修饰,让三个线程公用一把锁
  private static ReentrantLock lock = new ReentrantLock();
    private static int ticket = 100;

    @Override
    public void run () {
        while (true){
            try {
                lock.lock();
                if (ticket > 0){
                    System.out.println(Thread.currentThread().getName()+"正在售出第"+ticket+"张票");
                    ticket--;
                }else{
                    System.out.println("票已售完");
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
public class ThreadTest04 {
    public static void main ( String[] args ) {
        TicketThread th1 = new TicketThread();
        TicketThread th2 = new TicketThread();
        TicketThread th3 = new TicketThread();
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}

4.4 Lock和synchronized的区别

1. Lock:是显示锁(手动开启和关闭锁,)synchronized是隐式锁,出了作用域自动释放
2. Lock是代码块锁,synchronized有代码块和方法锁
3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性

5.线程的通信

5.1 等待唤醒机制

object中的三个方法:
1. wait   ():等待 使当前线程放弃同步锁并进入等待,直到其他线程进入此同步锁,并调用
		notify()或notifyAll()方法唤醒该线程为止
2.notify ()唤醒单个线程 唤醒此同步锁上第一个调用wait()的方法的线程
3.notifyAll( ) 唤醒全部线程  唤醒同步锁上调用wait()方法的所有线程
为什么wait().notify(),notifyall()定义在object中
  因为,这三个方法都必须在同步不代码块中调用,而且调用的对象是同步监听器,而同步监听器可以是任意的对象,所以,这三个方法定义在Object中
/**
 * 两个线程交替打印0-100内的自然数
 */
class Dayin implements Runnable{
    private int i;

    public void run () {
          while (i < 100){
              synchronized (this){
                  notify();
              System.out.println(Thread.currentThread().getName()+":"+i);
              i++;
                  try {
                      wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
        }
    }
}
public class RunnableTest01 {
    public static void main ( String[] args ) {
        Dayin dayin = new Dayin();
        new Thread(dayin,"线程1").start();
        new Thread(dayin,"线程2").start();
    }
}

5.2 sleep()和wait()的区别

相同点:一旦执行方法,都可以使当前线程进入阻塞状态
不同点:1.两个方法声明的位置不同,sleep可以在Thread类中声明,Object类中声明wait()
		2.sleep()可以在任何需要的场景下调用,wait()必须使用在同步代码块中。wait()的调用必须是同步代码块的锁对象
		3.sleep()方法不会释放同步锁,而wait()方法可以释放同步锁

6.新增的创建线程的方式

6.1 新增方式一: 实现Callable接口

//1. 实现Callable接口
class MyCallzble implements Callable<Integer>{
//2.重写里面的call方法
    public Integer call () throws Exception {
        int sum = 0;
        for (int i = 1;i< 100;i++){
            if (i%2==0){
                sum+=i;
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
        return sum;
    }
}
public class CallableTest {
    public static void main ( String[] args ) throws Exception {
        MyCallzble mc = new MyCallzble();
//        Future这个接口是用来操作Callable接口的数据,比如说,获取返回结果
//        但是Future是一个接口,不能直接new 对象,因此需要它的子实现类
        FutureTask<Integer>  fu = new FutureTask<Integer>(mc);
//        这里的参数本应该是Runnable接口的一个对象,但是由于FutureTask也实现了Runnable接口,所以
//        也可以作为参数,传入Thread的构造器中
        new Thread(fu,"线程一").start();
//        给主线程设置名称
        Thread.currentThread().setName("主线程");
//        获取Callable接口的call方法的返回结果
        Integer i = fu.get();
        System.out.println(Thread.currentThread().getName()+":"+i);
    }

6.2 Callable与Runnable

1.与run方法相比,可以有返回值
2.方法可以抛出异常
3.支持泛型的返回值
4.需要借助FutureTask类,比如说获取返回结果

6.3 新增方式二 :使用线程池

背景: 经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路?: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完之后,再返回线程池中,可以避免频繁创建和销毁线程,实现重复利用
使用线程池的优点:
	1.提高响应速度(减少了创建新线程使用的时间)
	2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
	3.便于线程管理
		corPoolSize:核心池的大小
		maximumPoolSize:最大线程数
		keepAliveTime:线程没有任务时,最多保持多长时间后会终止
class CallAble implements Callable{
    public Object call () throws Exception {
        Thread.currentThread().setName("Callable线程");
        System.out.println(Thread.currentThread().getName()+"实现了Callable接口");
        return null;
    }
}
class RunnAble implements Runnable{

    public void run () {
        Thread.currentThread().setName("Runable线程");
        System.out.println(Thread.currentThread().getName()+"实现了Runnable接口");
    }
}
public class FirstThreadPool {
    public static void main ( String[] args ) {
//        通过Executors工厂类来创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //用来Runnable和Callable,相当于new Thread(Runable/Callable).start
        service.submit(new CallAble());
//      只能用来操作Runnable
        service.execute(new RunnAble());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值