并发编程/多线程

目录

线程的创建的两种方式

线程的生命周期及其状态转换

线程调度

多线程同步

多线程通信


线程的创建的两种方式

1.继承java.lang包下的Thread类,覆写Thread类的run方法,在run方法中实现运行在线程上的代码

public class Example {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        while(true) {
            System.out.println("main方法在运行");
        }
    }
}
class MyThread extends Thread {
    public void run() {
        while(true) {
            System.out.println("MyThread类的run方法在运行");
        }
    }
}
/*
运行结果:main方法在运行、MyThread类的run方法在运行 都有输出
 */

2.继承java.lang包下的Runnable接口,在run方法中实现运行在线程上的代码

public class Example {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread);
        thread.start();
        while(true) {
            System.out.println("main方法在运行");
        }
    }
}
class MyThread implements Runnable {
    public void run() {
        while(true) {
            System.out.println("MyThread类的run方法在运行");
        }
    }
}
/*
运行结果:main方法在运行、MyThread类的run方法在运行 都有输出
 */

二者比较:

1)第一种方法有一定的局限性,因为java仅支持单一继承,一个类一旦继承了某个父类就无法再继承Thread类了。

2)实现Runnable接口 相对于 继承Thread类 来说,适合多个相同程序代码的线程去处理同一资源的情况(例如下面售票厅的四个售票窗口共同发售同一种的车票100张的代码:),把线程同程序代码、数据有效的分离,很好的体现了面向对象的设计思想。

public class Example {
    public static void main(String[] args) {
        TicketWindow tw = new TicketWindow();
        new Thread(tw, "窗口1").start();  //创建线程对象并命名窗口1,开启进程
        new Thread(tw, "窗口2").start();
        new Thread(tw, "窗口3").start();
        new Thread(tw, "窗口4").start();
    }
}
class TicketWindow implements Runnable {
    private int tickets = 100;
    public void run() {
        while(true) {
            if(tickets > 0) {
                Thread th = Thread.currentThread();  //获取当前进程
                String th_name = th.getName();
                System.out.println(th_name+"正在发售第"+tickets--"张票");
            }
            System.out.println("MyThread类的run方法在运行");
        }
    }
}

后台线程:

1)新创建的线程默认都是前台线程,如果某个线程对象在启动之前调用了setDaemon(true)语句(daemon:[计]守护进程),这个线程就变成一个后台线程。

2)只有后台线程时,进程就会结束。

3)要将某个线程置为后台线程(setDaemon),必须在该线程启动之前(start)。

线程的生命周期及其状态转换

当Thread对象创建完成时,线程的生命周期便开始了

并发编程/多线程_第1张图片

线程调度

分时调度、抢占式调度,后者根据优先级机制,是jvm默认调度模型。可以通过Thread类的setPriority(int newPriority)的方法对其进行设置,newPriority的值为1-10之间的数字或Thread类的三个静态常量(static int MAX_PRIORITY(10)/MIN_PRIORITY(1)/NORM_PRIORITY(5))。虽然java提供了10个线程优先级,但是这些优先级需要操作系统的支持,不同的操作系统并不一定可以和优先级一一对应,因此,在设计多线程程序时,其功能一定不能依赖于线程的优先级,而只能作为一种提高程序效率的手段。

线程休眠:

静态方法sleep(long millis),可以让当前在执行的线程暂停一段时间(millis参数毫秒)。休眠结束进入就绪状态。

线程让步:

通过yield()实现,和sleep方法有些类似,都可以让当前正在运行的线程暂停,区别在于yield方法不会阻塞线程,只是转换为就绪状态,让系统的调度器重新调度一次。当某个线程调用yield方法之后,只有与当前线程优先级相同或更高的线程才能获得执行机会。

线程插队:

当在某个线程中调用其他线程的join()方法时,调用的线程将被阻塞,直到被join()加入的线程执行完成后才会继续运行。

多线程同步

同步代码块:

限制某个资源在同一时刻只能被一个线程访问。

为了使处理共享资源的代码在任何时刻只能有一个线程访问,可将此块代码放置在用synchronized修饰的代码块中:

synchronized(lock) {

}

并发编程/多线程_第2张图片

public class Example {
    public static void main(String[] args) {
        TicketWindow tw = new TicketWindow();
        new Thread(tw, "窗口1").start();  //创建线程对象并命名窗口1,开启进程
        new Thread(tw, "窗口2").start();
        new Thread(tw, "窗口3").start();
        new Thread(tw, "窗口4").start();
    }
}
class TicketWindow implements Runnable {
    private int tickets = 100;
    Object lock = new Object();  //定义任意一个对象,作为同步代码块的锁
    public void run() {
        while(true) {
            synchronized{
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e){ //InterruptedException为中断异常,因为访问该代码的其他线程会发生阻塞
                    e.printStackTrace();
                }
                if(tickets > 0) {
                    Thread th = Thread.currentThread();  //获取当前进程
                    String th_name = th.getName();
                    System.out.println(th_name+"正在发售第"+tickets--"张票");
                } else {
                    break;
                }
            }
        }
    }
}

同步方法:

synchronized 返回值类型 方法名 ([参数...]){

}

该方法某一时刻只允许一个线程访问,访问该方法的其他线程会发生阻塞。

同步方法的锁就是就是当前调用该方法的对象,即this指向的对象。静态方法的锁是该方法所在类的class对象,该对象可以直接用"类名.class"的方式获取。

多线程通信

在Object类中提供了wait()、notify()(通告)、notifyAll()用于解决线程间通信问题,由于java中所有类都是Object类的子类或间接子类,因此任何类的实例都可以直接使用该方法。

void wait():使当前线程放弃同步锁并进入等待,知道其他线程进入此同步锁,并调用notify()方法或notifyAll()方法唤醒该进程为止

void notify():唤醒此同步锁上等待的第一个调用wait()方法的线程

void notifyAll():唤醒此同步锁上等待的所有调用wait()方法的线程

三个方法的调用者都应是同步锁对象,否则会抛出IllegalMonitorStateException异常。

例:两个线程同时去操作同一个存储空间(数组),其中一个线程负责向存储空间存入数据,另一个进程负责取出数据

并发编程/多线程_第3张图片

并发编程/多线程_第4张图片

你可能感兴趣的:(Java-多线程)