java多线程基础

一:线程的基础:

1.什么是进程?

一个进程对应一个应用程序。例如:在Windows操作系统启动Word加表示启动了一个进程。在java的开发环境下启动JVM,就表示启动了一个进程。

现代的计算机都是支持多进程的,在同一个操作系统中,可以同时启动多个进程。

2.多进程有什么作用?

单进程计算机只能做一件事情。玩电脑,一边玩游戏(游戏进程)一边听音乐(音乐进程)。

对于单核计算机来讲,在同一个时间点上,游戏进程和音乐进程是同时在运行吗?

不是,因为计算机的CPU只能在某个时间点上做一件事情。由于计算机的处理速度极高,在进程中频繁的切换执行,人无法感知,所以认为是“同时进行的”,实际上不是。

多进程的作用不是提高执行速度,而是提高CPU的使用率。

进程和进程之间的内存是独立的。

3.什么是线程?

线程是一个进程中的执行场景。一个进程可以启动多个线程。(进程是包含线程的)

例如:多台客户端电脑同时访问一台服务器上的应用程序。服务器CPU在多个线程中频繁切换。

4.多线程有什么作用? 多线程不是为了提高执行速度,而是为了提高应用程序的使用率。

线程和线程共享“堆内存和方法区内存”,栈内存是独立的。

5.java程序的运行原理?

java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,表示启动了一个进程。
该进程会自动启动一个"主线程",然后主线程调用某个类的main方法。所以main方法运行在主线程中。在此之前的所有程序都是程序都是单线程的,前几章讲的程序都是在main方法中运行的。

分析以下程序有几个线程?
以下程序只有一个线程,就是主线程
main、m1、m2、m3 这四个方法在同一个栈空间中,
main方法先压栈,调用m1(),m1压栈;m1调用m2,m2压栈;m2调用m3,m3压栈在栈顶。
m3方法最后压栈,最先执行完后弹栈。
没有启动其他任何线程


public class ThreadTest01 {
    public static void main(String[] args) {
        m1();
    }

    public static void m1(){
        m2();
    }

    public static void m2(){
        m2();
    }

    public static void m3(){
        System.out.println("m3...");
    }
}

二:多线程的实现

在java语言中实现多线程的第一种方法:

第一步:继承 java.lang.Thread;
第二步:重写 run 方法。

package com.company01.Thread;
/*
    如何创建线程?

    如何启动线程?

*/
public class ThreadTest02 {
    public static void main(String[] args) {

        // 创建线程
        Thread t = new Processor();

        // 启动
        t.start(); // 这段代码执行瞬间结束。告诉JVM再分配一个新的栈给t线程
                   // run不需要程序员手动调用,系统线程启动之后自动调用run方法。

        // 【注意】这里如果写 t.run(); 这是普通方法调用
        // 不会分配新的线程,此时mian和run在同一个栈中。
        // t.run();

        // 这段代码在主线程中运行
        for(int i=0; i<10; i++){
            System.out.println("main-->" + i);
        }

        // 有了多线程之后,main()方法结束只是主线程栈中没有方法栈帧了。
        // 但是其他线程或者其他栈中还有栈帧。
        // main方法结束,程序可能还在运行
        // 本程序输出顺序不唯一
    }
}

//定义一个线程
class Processor extends Thread{

    // 重写run方法
    public void run(){
        for(int i=0; i<10; i++){
            System.out.println("run-->" + i);
        }
    }
}

java中实现线程的第二种方法:

第一步:写一个类实现java.lang.Runnable; 接口
第二步:实现run方法

这种方式是推荐的,因为一个类实现接口之外,还保留了类的继承。

package com.company01.Thread;

public class ThreadTest03 {
    public static void main(String[] args) {

        // 创建线程
        Thread t = new Thread(new Processor01());

        // 启动
        t.start();
    }
}

// 这种方式是推荐的
class Processor01 implements Runnable{
    public void run(){
        for(int i=0; i<10; i++){
            System.out.println("run-->" + i);
        }
    }
}

三:线程常用方法

package com.company01.Thread;
/*

    本代码讲解三个方法:
        1.获取当前线程对象  Thread.currentThread()
        2.给线程取名字   线程引用.setName("t1");
        3.获取线程名字   线程引用.getName();
*/
public class ThreadTest04 {
    public static void main(String[] args) {

        // 如何获取当前线程对象?
        Thread t = Thread.currentThread();  // t保存的内存地址指向的线程是“主线程对象”

        // 获取线程的名字
        System.out.println(t.getName());  // main

        Processor02 p = new Processor02();
        Thread t1 = new Thread(p);
        System.out.println(t1.getName());  // Thread-0

        Thread t2 = new Thread(new Processor02());
        System.out.println(t2.getName());   // Thread-1
        // 设置线程的名字
        t2.setName("t2");

        System.out.println(t2.getName());   // t2

    }
}

class Processor02 implements Runnable{
    public void run(){
        Thread t = Thread.currentThread();       // t保存的内存地址指向的线程是“当前线程对象”
        System.out.println(t.getName());
    }
}

四:线程优先级

通常我们的计算机只有一个CPU,CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是
使用权,才可以执行指令。在单CPU的机器上线程不是并行运行的,只有在多个CPU上线程才可以并行。

JVM虚拟机要负责线程的调度,取得CPU的使用权,目前有两种调度模型:分时调度模型和抢占调度模型。
Java使用抢占式调度模型

分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级

高的线程获取的CPU时间片相对多一些。

优先级:1-10
最低:1
最高:10
默认:5

package com.company01.Thread;
/*
    
*/
public class ThreadTest05 {
    public static void main(String[] args) {

        System.out.println(Thread.MAX_PRIORITY);  // 10
        System.out.println(Thread.MIN_PRIORITY);  // 1
        System.out.println(Thread.NORM_PRIORITY); // 5

        // 注意此处Processor implements Runnable与Processor extends Thread的区别
        Thread t1 = new Thread(new Processor03());
        t1.setName("t1");

        Thread t2 = new Thread(new Processor03());
        t2.setName("t2");

        System.out.println(t1.getPriority());  // 5
        System.out.println(t2.getPriority());  // 5

        // 设置优先级
        t1.setPriority(1);
        t2.setPriority(10);

        // 启动线程
        t1.start();
        t2.start();
    }
}

class Processor03 implements Runnable{
    public void run(){
        for(int i=0; i<20; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

五:线程阻塞方法sleep()方法

package com.company01.Thread;
/*
    1.Thread.sleep(毫秒);
    2.sleep方法是一个静态方法。
    3.该方法的作用:阻塞当前线程。腾出CPU让给其他线程
*/
public class ThreadTest06 {
    public static void main(String[] args) throws InterruptedException{

        Thread t1 = new Processor04();
        t1.setName("t1");

        t1.start();

        //阻塞主线程
        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            Thread.sleep(500);
        }

    }
}

class Processor04 extends Thread{
    // 重写的方法不能比原先的方法抛出更加宽泛的异常
    // 而原始的Thread中run()根本就没有抛出异常,所以run方法不能用throws,只能用try...catch...
    public void run(){
        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            try {
                Thread.sleep(1000); // 让当前线程阻塞1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

注意:

package com.company01.Thread;

public class ThreadTest07 {
    public static void main(String[] args) throws InterruptedException{

        // 创建线程
        Thread t = new Processor05();
        t.setName("t");

        // 启动线程
        t.start();

        // 休眠
        t.sleep(5000);  // sleep()方法是静态方法,需要类名.的方式调用
                          // 此处等同于 Thread.sleep(5000); 阻塞的还是当前线程,和t线程无关

        System.out.println("Hello World!");

        A a = null;
        a.m1(); // 此处不会报空指针异常,m1()是静态方法,代码上写的是"a.m1()",实际上调用的是A.m1()
    }
}

class Processor05 extends Thread{
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println(Thread.currentThread().getName() + "-->" +i);
        }
    }
}

class A{
    public static void m1(){}
}

六:打断线程休眠

package com.company01.Thread;
/*
    某线程正在休眠,如何打断它的休眠

    以下方式依靠的是异常处理机制
*/
public class ThreadTest08 {
    public static void main(String[] args) throws InterruptedException{

        // 需求:启动线程,5s之后打断线程的休眠
        Thread t = new Thread(new Processor06());

        t.setName("t");

        t.start();

        // 5点之后
        Thread.sleep(5000);

        // 打断t的休眠
        t.interrupt();  // 利用的是异常处理机制 控制栏会输出异常
    }
}

class Processor06 implements Runnable{
    public void run(){
        try{
            Thread.sleep(100000000000L);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }

    }
}

动手例子:

package com.company01.Thread;
/*
    如何正确的更好的终止一个正在执行的线程。

    需求:线程启动五秒之后终止。
*/
public class ThreadTest09 {
    public static void main(String[] args) throws InterruptedException{

        Processor07 p = new Processor07();
        Thread t = new Thread(p);

        t.setName("t");

        t.start();

        // 5S之后终止
        Thread.sleep(5000);

        p.run = false;


    }
}

class Processor07 implements Runnable{

    boolean run = true;

    public void run(){
        for(int i=0; i<10; i++){

            if(run){
                try{
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "-->" + i);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }else{
                return;
            }
        }
    }
}

Thread.yield()方法

package com.company01.Thread;
/*
    Thread.yield()
    1、该方法是一个静态方法
    2、作用:给 同一个优先级的线程 让位,但是让位时间不固定。
    3、它与sleep()类似,只是不能由用户指定暂停多长时间

*/
public class ThreadTest10 {
    public static void main(String[] args) {
        Thread t = new Processor08();

        t.setName("t");

        t.start();

        // 主线程中
        for(int i=0; i<100; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

class Processor08 extends Thread{
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            if(i%20 == 0){
                Thread.yield();  // for每循环20次 停一次 但是好像计算机运行太快了 没有预期效果
            }
        }
    }
}

join() 合并线程

package com.company01.Thread;
/*
    (Thread引用).join();  成员方法

*/
public class ThreadTest11 {
    public static void main(String[] args) throws InterruptedException{

        Thread t = new Thread(new Processor10());

        t.setName("t");

        t.start();

        // 合并线程
        t.join();  // t和主线程合并,变为单线程的程序,两个栈空间变为一个栈区,需要等到t线程执行完后,才能继续下面的语句

        // 主线程
        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }

    }
}

class Processor10 implements Runnable{
    public void run(){
        for(int i=0; i<50; i++){
            try{
                Thread.sleep(1000);

            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "-->" + i);

        }
    }
}

七:线程同步机制

package com.company01.Thread;
/*
    以t1和t2为例:

        异步编程模型:t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁

        同步编程模型:t1线程和t2线程执行,当t1线程必须等t2线程执行结束之后,t1线程才能执行,这是同步编程模型。

        什么时候要同步呢?为什么要引入线程同步呢?
            1.为了数据的安全。尽管应用程序的使用降低,但是为了保证数据是安全的,必须加入线程同步机制。
            线程同步机制是程序变成了(等同)单线程。
             例如两个线程访问银行账户,银行存款1000,线程一取款500,在账户还没有更新时,如果采用非同步,
            线程二此时读取余额仍然为1000,所以需要采用同步编程模型,只有在线程一执行完后才能执行线程二。

            2.什么条件下要使用线程同步?
                第一:必须是多线程环境
                第二:多线程环境共享同一个数据
                第三:共享的数据涉及到修改操作

    以下程序演示取款例子。以下程序不使用线程同步机制,多线程同时对同一个账户进行取款操作,会出现什么问题?
*/
public class ThreadTest12 {
    public static void main(String[] args) {

        // 创造一个公共的账户
        Account act = new Account("act-001", 5000.0);

        //创建线程,共享同一个账户,对同一个账户进行取款
        /*
        ProcessorTest pt = new ProcessorTest(act);

        Thread t1 = new Thread(pt);
        Thread t2 = new Thread(pt);
        */
        // 或者
        Thread t1 = new Thread(new ProcessorTest(act));
        Thread t2 = new Thread(new ProcessorTest(act));

        t1.start();
        t2.start();

        /*
        取款1000.00成功,余额:4000.0
        取款1000.00成功,余额:4000.0
        */
    }
}

// 取款线程
class ProcessorTest implements Runnable{
    // 账户
    Account act;

    // Constructor
    ProcessorTest(Account act){
        this.act = act;
    }
    public void run(){
        act.withdraw(1000);
        System.out.println("取款1000.00成功,余额:" + act.getBalance());
    }
}
// 账户
class Account{
    private String actno;
    private double balance;

    public Account(){}
    public Account(String actno, double balance){
        this.actno = actno;
        this.balance = balance;
    }

    // setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }

    public void setBalance(double balance){
        this.balance = balance;
    }

    public String getActno(){
        return this.actno;
    }

    public double getBalance(){
        return this.balance;
    }

    // 对外提供一个取款的方法
    public void withdraw(double money){  // 对象当前账户取款

        double after = balance - money;
        // 更新
        // 延迟
        try{
            Thread.sleep(1000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        this.setBalance(after);
    }

}

线程同步编程实例一

package com.company01.Thread;
/*
    以下程序演示取款例子。以下程序使用线程同步机制.

    与上一个例子相比:
        ThreadTest12执行时间需要1秒
        而ThreadTest13需要2秒,但是数据是安全的

    把需要同步的代码,放到同步语句块中(synchronized(){}语句块中)

            原理:t1线程和t2线程
            t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁,
            如果找到this对象锁,则会进入同步语句块中执行程序。当同步语句块中的代码
            执行结束之后,t1线程归还this的对象锁。

            在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到了
            synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程
            持有,只能在这等到this对象锁的归还。

            所以永远只有一个线程在执行
*/
public class ThreadTest13 {
    public static void main(String[] args) {

        // 创造一个公共的账户
        Account01 act = new Account01("act-001", 5000.0);

        //创建线程,共享同一个账户,对同一个账户进行取款

        Thread t1 = new Thread(new ProcessorTest01(act));
        Thread t2 = new Thread(new ProcessorTest01(act));

        t1.start();
        t2.start();
        t1.setName("t1");
        t2.setName("t2");

        /*
        取款1000.00成功,余额:4000.0
        取款1000.00成功,余额:3000.0
        */
    }
}

// 取款线程
class ProcessorTest01 implements Runnable{
    // 账户
    Account01 act;

    // Constructor
    ProcessorTest01(Account01 act){
        this.act = act;
    }
    public void run(){
        act.withdraw(1000);
        System.out.println(Thread.currentThread().getName() + "取款1000.00成功,余额:" + act.getBalance());
    }
}
// 账户
class Account01{
    private String actno;
    private double balance;

    public Account01(){}
    public Account01(String actno, double balance){
        this.actno = actno;
        this.balance = balance;
    }

    // setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }

    public void setBalance(double balance){
        this.balance = balance;
    }

    public String getActno(){
        return this.actno;
    }

    public double getBalance(){
        return this.balance;
    }

    // 对外提供一个取款的方法
    public void withdraw(double money){  // 对象当前账户取款
        //把需要同步的代码,放到同步语句块中
        /*
            原理:t1线程和t2线程
            t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁,
            如果找到this对象锁,则会进入同步语句块中执行程序。当同步语句块中的代码
            执行结束之后,t1线程归还this的对象锁。

            在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到了
            synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程
            持有,只能在这等到this对象锁的归还。

        */
        synchronized(this){
            double after = balance - money;
            // 更新
            // 延迟
            try{
                Thread.sleep(1000);
                // 【注意】如果这里没有使用sleep,会导致控制台打印输出顺序混乱
                // 同步只是到了this.setBalance,也就是只是将线程中run()方法的act.withdraw(1000);这一句同步
                // 并没有将输出也列入到同步中,因此输出这一句话仍然是多线程,可能是t1先输出,也可能是t2先先输出
                // 而这里加入1S的延迟,在t1延迟结束后执行完setBalance归还对象锁,t2获得对象锁,t2就进入了1S的延迟
                // 这1S的时间足以让 t1线程执行完run里面方法,完成控制台的打印输出,1S后t2才能完成打印输出。
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            this.setBalance(after);

        }

    }

}

线程同步编程实例二

package com.company01.Thread;
/*
    以下程序演示取款例子。以下程序使用线程同步机制.

    与上一个例子相比:
        ThreadTest12执行时间需要1秒
        而ThreadTest13需要2秒,但是数据是安全的

    把需要同步的代码,放到同步语句块中(synchronized(){}语句块中)

            原理:t1线程和t2线程
            t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁,
            如果找到this对象锁,则会进入同步语句块中执行程序。当同步语句块中的代码
            执行结束之后,t1线程归还this的对象锁。

            在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到了
            synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程
            持有,只能在这等到this对象锁的归还。

            所以永远只有一个线程在执行
*/
public class ThreadTest14 {
    public static void main(String[] args) {

        // 创造一个公共的账户
        Account02 act = new Account02("act-001", 5000.0);

        //创建线程,共享同一个账户,对同一个账户进行取款

        Thread t1 = new Thread(new ProcessorTest02(act));
        Thread t2 = new Thread(new ProcessorTest02(act));

        t1.start();
        t2.start();
        t1.setName("t1");
        t2.setName("t2");

        /*
        取款1000.00成功,余额:4000.0
        取款1000.00成功,余额:3000.0
        */
    }
}

// 取款线程
class ProcessorTest02 implements Runnable{
    // 账户
    Account02 act;

    // Constructor
    ProcessorTest02(Account02 act){
        this.act = act;
    }
    public void run(){
        act.withdraw(1000);
        System.out.println(Thread.currentThread().getName() + "取款1000.00成功,余额:" + act.getBalance());
    }
}

// 账户
class Account02{
    private String actno;
    private double balance;

    public Account02(){}
    public Account02(String actno, double balance){
        this.actno = actno;
        this.balance = balance;
    }

    // setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }

    public void setBalance(double balance){
        this.balance = balance;
    }

    public String getActno(){
        return this.actno;
    }

    public double getBalance(){
        return this.balance;
    }

    // 对外提供一个取款的方法
    // 这里也可以在方法的修饰符列表中加入关键字synchronized
    // 这时整个方法都是同步编程的
    public synchronized void withdraw(double money){  // 对象当前账户取款

            double after = balance - money;
            // 更新
            // 延迟
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            this.setBalance(after);
            
    }

}

面试题

package com.company01.Thread;
/*
    面试题
*/
public class ThreadTest15 {
    public static void main(String[] args) throws InterruptedException{
        MyClass mc = new MyClass();
        Thread t1 = new Thread(new Processor13(mc));
        t1.setName("t1");
        Thread t2 = new Thread(new Processor13(mc));
        t2.setName("t2");
        /*
        // 如果这里是这样写的
        MyClass mc1 = new MyClass();
        MyClass mc2 = new MyClass();
        Thread t1 = new Thread(new Processor13(mc1));
        Thread t2 = new Thread(new Processor13(mc2));
        // 此时无论m2有没有synchronized修饰,m1的执行并不会影响m2
        // 因为他们两个线程没有共享同一个对象,两个线程的对象锁不一样
        */
        t1.start();
        Thread.sleep(1000);
        t2.start();   // m2方法上没有synchronized关键字,调用的时候不需要找对象锁。
                      // 所以这里是m2执行不需要等m1结束
                      // 如果在m2方法加上synchrinized关键字,m1和m2共享同一个对象,就需要对象锁
        /*
        m2.....
        m1.....
        */

    }
}

class Processor13 implements Runnable{
    MyClass mc;
    Processor13(MyClass mc){
        this.mc = mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.m1();
        }

        if(Thread.currentThread().getName().equals("t2")){
            mc.m2();
        }
    }
}

class MyClass {
    public synchronized void m1(){
        // 休眠
        try{
            Thread.sleep(10000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("m1.....");
    }

    public void m2(){
        System.out.println("m2.....");
    }
}

类锁

例子一

package com.company01.Thread;
/*
    类锁,类只有一个,所以锁是类级别,只有一个
*/
public class ThreadTest16 {
    public static void main(String[] args) throws InterruptedException{

        Thread t1 = new Thread(new Processor14());
        Thread t2 = new Thread(new Processor14());

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}
class Processor14 implements Runnable{
    public void run(){
        if("t1".equals(Thread.currentThread().getName())){
            MyClass01.m1();
        }
        if("t2".equals(Thread.currentThread().getName())){
            MyClass01.m2();
        }
    }
}
class  MyClass01{
    // synchronized 添加到静态方法上,线程执行此方法的时候会找类锁。
    public synchronized static void m1(){
        try{
            Thread.sleep(10000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("m1.....");
    }
    // 不会等m1结束,因为该方法没有被synchronized修饰
    /*
    public static void m2(){
        System.out.println("m2.....");
    }
    */

    // m2方法等m1结束之后才能执行,该方法有syschronized
    // 线程执行该代码需要“类锁”,而类锁只有一个。
    public synchronized static void m2(){
        System.out.println("m2.....");
    }
}

例子二

package com.company01.Thread;
/*
    类锁,类只有一个,所以锁是类级别,只有一个
*/
public class ThreadTest17 {
    public static void main(String[] args) throws InterruptedException{

        MyClass02 mc1 = new MyClass02();
        MyClass02 mc2 = new MyClass02();

        Thread t1 = new Thread(new Processor15(mc1));
        Thread t2 = new Thread(new Processor15(mc2));

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

class Processor15 implements Runnable{
    MyClass02 mc;
    Processor15(MyClass02 mc){
        this.mc = mc;
    }
    public void run(){
        if("t1".equals(Thread.currentThread().getName())){
            mc.m1();  // 尽管静态方法也可以用 “引用.”的方法调用
                      // 但是底层用的还是 “类名.”的方式
                      // 用的还是 类锁 ,和对象锁无关
        }
        if("t2".equals(Thread.currentThread().getName())){
            mc.m2();
        }
    }
}

class  MyClass02{
    // synchronized 添加到静态方法上,线程执行此方法的时候会找类锁。
    public synchronized static void m1(){
        try{
            Thread.sleep(10000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("m1.....");
    }

    // 要等m1执行完之后,归还类锁,才能继续执行m2方法
    public synchronized static void m2(){
        System.out.println("m2.....");
    }
}

死锁

package com.company01.Thread;
/*
    死锁:
        线程1和线程2都想锁住对象1和对象2
        线程1先锁住对象1,线程2先锁住对象2
        线程1想锁住对象2,需要线程2先归还对象2的对象锁
        线程2想锁住对象1,需要线程1先归还对象1的锁

*/
public class ThreadTest18 {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        Thread t1 = new Thread(new T1(o1, o2));
        Thread t2 = new Thread(new T2(o1, o2));

        t1.start();
        t2.start();

    }
}

class T1 implements Runnable{
    Object o1;
    Object o2;

    T1(Object o1, Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run(){
        synchronized (o1){
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            synchronized (o2){

            }
        }
    }
}

class T2 implements Runnable{
    Object o1;
    Object o2;

    T2(Object o1, Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run(){
        synchronized (o2){
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            synchronized (o1){

            }
        }
    }

}

守护线程

package com.company01.Thread;
/*
    守护线程:
        从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。
        守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,
        只要有一个用户线程存在,那么守护线程就不会结束。

        例如java中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,
        它才会结束。

        守护线程一般是无限循环执行的。

        java在执行的时候,会启动JVM虚拟机进程,而JVM在启动的时候,不只是启动一个线程,
        其中有一个主线程,还有垃圾回收线程,他们在不同的栈空间里面。
*/
public class ThreadTest19 {
    public static void main(String[] args) throws InterruptedException{
        Thread t1 = new Processor17();

        t1.setName("t1");

        // 设置t1线程为守护线程
        t1.setDaemon(true);

        t1.start();

        // 主线程
        for(int i=0; i<10; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            Thread.sleep(1000);
        }
    }
}

/*
    若这里t1为普通线程:
        run()函数里面while(true),该线程永远都不会结束,无线循环下去。

    而这里将t1设置为守护线程之后:
        在主线程结束之后,程序里面再也没有其他用户线程,守护线程就会自动退出。
*/

class Processor17 extends Thread{
    public void run(){
        int i = 0;
        while(true){
            i++;
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            try{
                Thread.sleep(500);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

八:定时器

package com.company01.Thread;
/*
    关于定时器的应用:
    作用:每隔一段固定的时间执行一段固定的代码。

    构造方法:
        Timer()  创建一个新的定时器
        Timer(boolean isDaemon) 创建一个新的定时器,可以指定其相关的线程作为守护程序运行
        Timer(String name)  创建一个新的定时器,其相关的线程具有指定的名称
        Timer(String name, boolean isDaemon)
*/
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.text.*;
public class TimerTest01 {
    public static void main(String[] args) throws ParseException {

        // 1.创建定时器
        Timer t = new Timer();

        // 2.指定定时任务
        // schedule(TimerTask task, Data firstTimer, long period)
        t.schedule(
                new LogTimerTask(),
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2019-12-18 20:30:00 000"),
                1000*2
        );

    }
}

// 指定任务
class LogTimerTask extends TimerTask{
    public void run(){
        // 输出当前时间
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值