Java多线程

多线程

1.进程和线程

  • 进程是一个应用程序(1个进程是一个软件)
  • 线程是一个进程中的执行场景/执行单元
  • 一个进程可以启动多个线程。
  • 两者关系:进程A和进程B内存独立不共享。线程A和线程B 堆内存和方法区内存共享,栈内存独立,一个线程一个栈,各栈之间互不干扰,这就是多线程并发。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWIkp7IL-1602401030284)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200927102902302.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7H082ALo-1602401030286)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200927103521561.png)]

package com.codesing;
/*
* 除垃圾回收线程外,有几个线程??????
* 1个,只有一个主线程main,只有一个栈
* */
public class ThreadTest01 {
    public static void main(String[] args) {
        System.out.println("main-begin");
        m1();
        System.out.println("main-over");
    }

    private static void m1() {
        System.out.println("m1-begin");
        m2();
        System.out.println("m1-over");
    }

    private static void m2() {
        System.out.println("m2-begin");
        m3();
        System.out.println("m2-over");
    }

    private static void m3() {
        System.out.println("m3-begin");
        System.out.println("m3-over");
    }
}

2.java中实现线程方式

  1. 编写一个类,继承java.lang.Thread,重写run()方法。
package com.codesing;
/*
* 1.myThread.start();这段代码不结束,for (int i = 0; i < 100; i++) 这段代码不会执行???????
* 2.是的,在方法体中,代码始终是自上而下执行的。只不过myThread.start()瞬间结束而已。
* */
public class ThreadTest02 {
    //这是主线程
    public static void main(String[] args) {
        //新建一个分支线程
        MyThread myThread = new MyThread();
//        myThread.run();//这里只是调用方法,没有开辟栈空间,没有启动线程,(只是单线程)
        //启动线程:star():启动一个分支线程,未其在JVM中开辟一个新栈空间,执行完瞬间结束。
        //线程启动,自动调用run()方法,而且run()方法在分支栈底部(压栈)
        //main()方法在主栈底部,run()方法在分栈底部,main()和run()是平级的。
        myThread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程---》"+ i);
        }
    }
}
//这是分支线程
class MyThread extends Thread{
    @Override
    public void run() {//这个方法必须重写,相当于main()方法一样
        for (int i = 0; i <100 ; i++) {
            System.out.println("分支线程----》"+i);
        }
    }
}
  1. 编写一个类,实现java.lang.Runnable接口,重写run方法(可以匿名内部类实现)
package com.codesing;
/*
* 第二种实现接口:Runnable
* */
public class ThreadTest03 {
    public static void main(String[] args) {
        //创建一个可运行对象
        Myrunnable myrunnable = new Myrunnable();
        //封装成一个线程对象
        Thread thread = new Thread(myrunnable);
        //Thread thread = new Thread(new Myrunnable());//这是合并写法
        
        //启动线程
        thread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程---》"+ i);
        }
    }
}
//创建可运行类
class Myrunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("分支线程---》"+ i);
        }
    }
}

注意:一般会用第二种方法,因为实现了一个类,还可以继承其他类,而java是一个单继承模式,第一用种方式有局限性。

3.线程生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YgfMoRIB-1602401030288)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200927133343653.png)]

  • 新建状态-就绪状态-阻塞状态-运行状态-死亡状态

4.常用方法

4.1 Thread.currentThread()

package com.codesing;
/*
* static Thread.currentThread() 静态方法,获取当前线程对象
* */
public class ThreadTest04 {
    public static void main(String[] args) {
        //获取当前线程对象引用
        Thread cruuentthread= Thread.currentThread();
        System.out.println("当前线程名字:"+ cruuentthread.getName());
        //创建一个线程对象
        MyThread01 myThread01 = new MyThread01();
        //获取线程名字
        System.out.println(myThread01.getName());//Thread-0
        //设置线程名字
        myThread01.setName("t1");
        //再次获取线程名字
        System.out.println(myThread01.getName());//t1
        //启动线程
        myThread01.start();

        //创建一个线程对象
        MyThread01 myThread02 = new MyThread01();
        //获取线程名字
        System.out.println(myThread02.getName());//Thread-1
        //设置线程名字
        myThread02.setName("t2");
        //再次获取线程名字
        System.out.println(myThread02.getName());//t2
        myThread02.start();

    }
}
class MyThread01 extends Thread{
    @Override
    public void run() {
        //获取当前线程对象的引用
        System.out.println("当前线程名字:"+ Thread.currentThread().getName());
    }
}

4.2 sleep(long millis)

package com.codesing;
/*
* 1. static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
* 2. 让当前线程进入休眠,处于”阻塞状态“,放弃占有CPU时间片,让其他线程使用。
* 3. 在哪个线程调用,就让这个休眠
* sleep()方法:可以做到:间隔特定时间,去执行特定代码,每隔多久执行一次。
* */
public class ThreadTest05 {
    public static void main(String[] args) {
//        try {
//            Thread.sleep(1000*5);//让当前线程休眠5秒
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        //5秒之后执行这行代码
//        System.out.println("hello,world!");
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--->"+i);
        }
    }
}
package com.codesing;

public class ThreadTest05_1 {
    public static void main(String[] args) {
        //sleep面试题:
        Thread t  = new MyThread02();
        t.setName("t");
        t.start();
        try {//这里会让t线程休眠吗??????
            t.sleep(1000*5);//在执行的时候,还是会转换成:Thread.sleep()执行
                            //让当前线程进入休眠状态,当前线程是main。所以会让main线程休眠5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //5秒之后执行
        System.out.println("Hello main Sleep over!");
    }
}
class MyThread02 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName()+"--->"+ i);
        }
    }
}

4.3 interrupt()

package com.codesing;
/*
* 如何唤醒(中断睡眠)一个正在睡眠的线程。
* 不是中断线程的执行,只是终止线程睡眠。
*
* */
public class ThreadTest05_2 {
    public static void main(String[] args) {
        //创建一个线程对象
        Thread t = new Thread(new myRuunable01());
        t.setName("t");
        //启动线程
        t.start();
        //希望5秒后t线程醒来
        try {
            Thread.sleep(1000*5);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //5秒之后t中断睡眠(这种中断方式靠java的异常机制,打印异常信息)
        t.interrupt();//干扰,会让
    }
}
class myRuunable01 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"----->begin");
        //这里run()方法:只能tyr catch,不能在方法上throws异常。
        //因为,run()方法在父类中没有抛出异常,子类不能比父类抛出更多的异常
        try {
            Thread.sleep(1000*24*60*60*365);//让这个线程睡一年
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //其他方法可以throws,因为不是继承的方法,但在这里只能try catch,不能在类上抛出。
        try {
            doSome();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"----->end");
    }
    //这个方法可以抛出throsws异常,
    private void doSome() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("我怎么被叫醒了");
    }
}

4.4 合理终止一个线程

package com.codesing;
/*
* 合理终止一个线程:
* 1.stop()方法:已弃用,可能会丢失数据
* 2.设置一个布尔标识,推荐
* */
public class ThreadTest06 {
    public static void main(String[] args) {
        myRuun r = new myRuun();
        Thread t = new Thread(r);
        t.setName("t");
        t.start();
        //模拟5秒
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("caonima");
        //终止t线程
       r.run = false;
    }
}
class myRuun implements Runnable{

    //打一个布尔标识
    boolean run = true;
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (run) {
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                return;
            }
        }
    }
}

4.5 线程调度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BjAqWzev-1602401030290)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928124350056.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7MsdMj5n-1602401030292)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928124503682.png)]

4.5 优先级问题

package com.codesing;

public class ThreadTest07 {
    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
        //设置线程优先级
        Thread.currentThread().setPriority(1);
        System.out.println(Thread.currentThread().getPriority());//1

        myRun7 r = new myRun7();
        Thread thread = new Thread(r);
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"线程:"+i);
        }
    }
}
class myRun7 implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getPriority());
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"线程:"+i);
        }
    }
}

4.6 线程让位Thread.yield()

package com.codesing;
/*
* 线程让位:静态方法 Thread.yield()
* 当前线程暂停,回到就绪状态,让给其他线程
* */
public class ThreadTest08 {
    public static void main(String[] args) {
        Thread thread = new Thread(new myRun8());
        thread.setName("t");
        thread.start();
        for (int i = 0; i < 10000; i++) {
            System.out.println(Thread.currentThread().getName()+"---->"+i);
            }
        }
}
class myRun8 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%100 == 0){//每100让位一次
                Thread.yield();//当前线程暂停,让位给主线程
                continue;
            }
            System.out.println(Thread.currentThread().getName()+"---->"+i);
        }
    }
}

4.7 线程合并:Thread.join()

package com.codesing;
/*
* 线程合并:Thread.join()
* */
public class ThreadTest09 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"----begin");
        Thread t = new Thread(new myRun9());
        t.start();
        //合并线程:阻塞当前线程,t线程执行,直到t结束,当前线程继续执行
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"----over");//始终最后输出
    }
}
class myRun9 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++) {
            System.out.println(Thread.currentThread().getName()+"---->"+i);
        }
    }
}

5.线程安全

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5uZOKwOd-1602401030294)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928134430993.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFZlZK8H-1602401030295)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928134512278.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8391dLy8-1602401030296)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928134531289.png)]

5.1 模拟账户取款

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21qxJFAM-1602401030297)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928144605807.png)]

package com.codesing.ThreadSafe2;

public class Application {
    public static void main(String[] args) {
        //创建一个账户
        Account account = new Account("account", 10000);

        //创建两个线程
        ThreadAccount user1 = new ThreadAccount(account);
        ThreadAccount user2 = new ThreadAccount(account);
        user1.setName("user1");
        user2.setName("user2");
        //启动线程取款
        user1.start();
        user2.start();
    }
}

package com.codesing.ThreadSafe2;

public class ThreadAccount extends Thread{
    //共享同一个账户
    private Account account;
    //通过构造方法传递过来账户对象
    public ThreadAccount(Account account){
        this.account = account;
    }
    @Override
    public void run() {
        //执行取款操作,假设取5000
        int money = 5000;
        account.withDraw(money);
        System.out.println(Thread.currentThread().getName()+"对"+account.getActno()+"取款后,剩余:"+account.getBalance());
    }
}

  • 未使用同步机制(E:\学习文档\数据库课程设计\IDE-Workspace\java多线程\001-java多线程\src\com\codesing\ThreadSafe)
package com.codesing.ThreadSafe;
/*
 * 银行取款:
 * 1.未使用线程同步机制,多线程对同一个账户取款,出现线程安全问题
 *   ======================
 *   user1取款后,剩余:5000
 *   user2取款后,剩余:5000
 *   =======================
 *
 * */
public class Account {
    private String actno;//账号名
    private int balance;//余额

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


    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }
    //取款方法:
    public void withDraw(int money){
        //user1,user2线程并发取款,(user1,user2两个栈,操作堆中同一个对象。)
        //取款前余额
        int before = this.balance;
        //取款后余额
        int after = this.balance - money;
        //设置网络延迟,100%出问题
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //更新余额
        //思考:t1执行到这里,没来及更新,t2线程执行withDraw了。此时会出问题
        this.setBalance(after);
    }
}
  • 使用同步机制(E:\学习文档\数据库课程设计\IDE-Workspace\java多线程\001-java多线程\src\com\codesing\ThreadSafe2)
package com.codesing.ThreadSafe2;
/*
 * 银行取款:
 *.使用线程同步机制synchronized(),解决线程安全问题
 *   ======================
 *   user1对account取款后,剩余:5000
 *   user2对account取款后,剩余:0
 *   =======================
 *
 * */
public class Account {
    private String actno;//账号名
    private int balance;//余额

    public Account(String actno, int balance) {
        this.actno = actno;
        this.balance = balance;
    }
    // 1. Object obj = new Object();//实例变量(Account对象是多线程共享的,则Account对象中的实例变量obj对象也是共享的)
    //所以,下面代码synchronized (obj)写成这样也是可以的,因为共享线程会排队。


    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }
    //取款方法:
    public void withDraw(int money   
   /*
        * 2.synchronized (actno)//这么写也能做到线程排队,actno也是一个共享对象。
        * 3.synchronized ("abc")//这样写,abc在字符串常量池中,所有线程共享,这样会导致,所有线程同步(一人取款,全天下人等)
        * 4.synchronized (null)// 空指针异常,报错
   */
        // 5.Object obj1= new Object();//这里obj是一个局部变量,不是共享对象,synchronized (obj1 )这样写就不安全了,没有排队,各new各的变量。
                         
         //以下几行代码排队执行,不能并发。
        synchronized (this){//这里账户对象共享。this(不一定是this,只要是共享对象就可以)
            int before = this.balance;
            int after = before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
        }
    }
}

5.2 synchronized()详解

 synchronized (){
                线程同步代码块
            }
synchronized (),括号中写什么????
    必须是多线程共享的数据,才能达到线程同步,假设线程t1,t2,t3,t4,t5,你只想要t1,t2,t3排队,t4,t5不排队。
    那么,你就在()中写一个t1,t2,t3,共享的对象。而这个对象对t4,t5不共享。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-578aYdPl-1602401030298)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928135726983.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jj6JGq5t-1602401030300)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928135944289.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KXBVmLlY-1602401030301)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928140046045.png)]

5.3 哪些变量存在线程安全问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgDYyYkZ-1602401030302)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928140250045.png)]

5.4 扩大synchronized范围

ThreadAccount 修改

package com.codesing.ThreadSafe2;

public class ThreadAccount extends Thread{
    //共享同一个账户
    private Account account;
    //通过构造方法传递过来账户对象
    public ThreadAccount(Account account){
        this.account = account;
    }
    @Override
    public void run() {
        //执行取款操作,假设取5000
        int money = 5000;
        //synchronized (this)这里this就不行了,因为this---->ThreadAccount.而我们是两线程共享一账户,
        // 这个就相当于各自创建各自线程,两个栈,不存在排队。
        synchronized (account) {//扩大范围,但效率更低了
            account.withDraw(money);
        }
        System.out.println(Thread.currentThread().getName()+"对"+account.getActno()+"取款后,剩余:"+account.getBalance());
    }
}

局部变量和常量不存在线程安全问题。成员变量可能有线程安全问题。

5.5 方法上加synchronized

 //取款方法:
    //synchronized出现在实例方法上一定锁的是this.
    //缺点:不灵活。导致整个方法都需要同步,扩大同步范围,降低效率。
    //优点: 代码节俭。如果共享的对象就是this,且整个方法都需要同步,选这种
    public synchronized void  withDraw(int money){
            int before = this.balance;
            int after = before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
//        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6anuBppQ-1602401030303)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928150523450.png)]

5.6 synchronized三种写法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dBsqAOO-1602401030304)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928150904948.png)]

6.面试题

(一个厕所门,两个隔间,一个隔间上锁,另一个隔间没锁)

package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 不需要。doOther没有synchronized,不需要排队
* */
public class Exam01 {
    public static void main(String[] args) {
        myClass mc = new myClass();

        Thread t1 = new myThread(mc);
        Thread t2 = new myThread(mc);

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

        t1.start();
        try {
            Thread.sleep(1000);//保证t1执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}
class myThread extends Thread{
    private  myClass mc;
    public myThread(myClass mc){
        this.mc = mc;
    }
    public void run(){
        if (Thread.currentThread().getName() == "t1"){
            mc.doSome();
        }
        if (Thread.currentThread().getName() == "t2"){
            mc.doOther();
        }
    }
}
class myClass{
    public synchronized void doSome(){
        System.out.println("doSome------>begin");
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome----->over");
    }
    public void doOther(){
        System.out.println("doOther------>begin");
        System.out.println("doOther----->over");
    }
}

(一个厕所门,两个隔间,门上上锁)

package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 需要。doOther有synchronized
* */
public class Exam01 {
    public static void main(String[] args) {
        myClass mc = new myClass();

        Thread t1 = new myThread(mc);
        Thread t2 = new myThread(mc);

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

        t1.start();
        try {
            Thread.sleep(1000);//保证t1执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}
class myThread extends Thread{
    private  myClass mc;
    public myThread(myClass mc){
        this.mc = mc;
    }
    public void run(){
        if (Thread.currentThread().getName() == "t1"){
            mc.doSome();
        }
        if (Thread.currentThread().getName() == "t2"){
            mc.doOther();
        }
    }
}
class myClass{
    public synchronized void doSome(){
        System.out.println("doSome------>begin");
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome----->over");
    }
    public synchronized void doOther(){
        System.out.println("doOther------>begin");
        System.out.println("doOther----->over");
    }
}

(两个厕所门,每个厕所两个隔间,两个门上上锁)

package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 不需要。两个myClass对象,两把锁,不存在排队
* */
public class Exam01 {
    public static void main(String[] args) {
        myClass mc1 = new myClass();
        myClass mc2 = new myClass();

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

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

        t1.start();
        try {
            Thread.sleep(1000);//保证t1执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}
class myThread extends Thread{
    private  myClass mc;
    public myThread(myClass mc){
        this.mc = mc;
    }
    public void run(){
        if (Thread.currentThread().getName() == "t1"){
            mc.doSome();
        }
        if (Thread.currentThread().getName() == "t2"){
            mc.doOther();
        }
    }
}
class myClass{
    public synchronized void doSome(){
        System.out.println("doSome------>begin");
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome----->over");
    }
    public synchronized void doOther(){
        System.out.println("doOther------>begin");
        System.out.println("doOther----->over");
    }
}

(一个厕所门,两个隔间,门上上锁,天下人只有一个厕所)

package com.codesing.examHR;
/*面试题:
* doOther方法执行需要等待doSome方法的结束吗?
* 需要,静态方法是类锁,不管创建了几个对象,类锁只有一把。
* */
public class Exam01 {
    public static void main(String[] args) {
        myClass mc1 = new myClass();
        myClass mc2 = new myClass();

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

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

        t1.start();
        try {
            Thread.sleep(1000);//保证t1执行完
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}
class myThread extends Thread{
    private  myClass mc;
    public myThread(myClass mc){
        this.mc = mc;
    }
    public void run(){
        if (Thread.currentThread().getName() == "t1"){
            mc.doSome();
        }
        if (Thread.currentThread().getName() == "t2"){
            mc.doOther();
        }
    }
}
class myClass{//静态方法加锁,是类锁,只有一把锁
    public synchronized static void doSome(){
        System.out.println("doSome------>begin");
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome----->over");
    }
    public synchronized static void doOther(){
        System.out.println("doOther------>begin");
        System.out.println("doOther----->over");
    }
}

7.搞清楚

  • 排它锁(synchronized属于这个),互斥锁
  • 对象锁(一个对象一个锁,100个对象100个锁),类锁(天下对象一个锁)

8.死锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XlcsZtuC-1602401030306)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928154941548.png)]

t1自上往下锁,t2自下往上锁。每个只锁住一个,另一个僵持住了。

package com.codesing.deadlock;
/*
* 死锁很难调试,会一直处于运行状态,会写。
* */
public class DeadLock {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        //t1,t2线程共享o1,o2
        myThread1 t1 = new myThread1(o1,o2);
        myThread2 t2 = new myThread2(o1,o2);
        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        t2.start();
    }
}
class myThread1 extends Thread{
    Object o1;
    Object o2;
    public myThread1(Object o1,Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run(){//先锁o1,再锁o2
        synchronized (o1){
            try {
                Thread.sleep(1000);//保证锁住o1
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2){
            }
        }
    }
}
class myThread2 extends Thread{
    Object o1;
    Object o2;
    public myThread2(Object o1,Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }
    public void run(){//先锁o2,再锁o1
        synchronized (o2){
            try {
                Thread.sleep(1000);//保证锁住o2
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1){
            }
        }
    }
}

9.聊聊

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhYup34E-1602401030307)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928161708466.png)]

10.守护线程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JPRtkD3S-1602401030308)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928184358745.png)]

package com.codesing;
//守护线程
public class ThreadGuard {
    public static void main(String[] args) {
        myThread t = new myThread();
        t.setName("守护线程");
        //启动线程前,设置为守护线程
        t.setDaemon(true);//主线程结束,守护线程结束。

        t.start();
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"---->"+i);
        }
    }
}
class myThread extends Thread{
    public void run(){
        int i = 0;
        while (true){
            System.out.println(Thread.currentThread().getName()+"---->"+(++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

11.定时器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFFDWVNl-1602401030309)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928194927504.png)]

package com.codesing;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 制作一个定时器 new Timer
 *
 */
public class TimerTest {
    public static void main(String[] args)  {

        Timer timer = new Timer();
       // new Timer(true);也可以设置为守护线程

        //timer.schedule(定时任务,开始时间,间隔);
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date firstTime = simpleDateFormat.parse("2020-09-28 20:50:00");
            timer.schedule(new myTask(),firstTime,1000*10);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
//定义定时任务
class myTask extends TimerTask{

    @Override
    public void run() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date time = new Date();
        String now = sdf.format(time);
        System.out.println(now+"数据备份了一次!!!");
    }
}
/*
2020-09-28 20:50:00数据备份了一次!!!
2020-09-28 20:50:10数据备份了一次!!!
2020-09-28 20:50:20数据备份了一次!!!
2020-09-28 20:50:30数据备份了一次!!!
2020-09-28 20:50:40数据备份了一次!!!
*/

12.实现线程的第三种方法

package com.codesing;
/*
* 实现线程第三种方法:
*       实现Callable接口
*       优点:能获取到线程返回结果
*       缺点:执行效率低,在获取t线程执行结果时,当前线程阻塞。
* */
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//JUC包下的,属于java并发包,老JDK没有这个包,8新特性

public class ThreadCallable {
    public static void main(String[] args) {
        //创建一个”未来类“对象
        //参数实现Callable接口
        FutureTask task = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {//类似于run(),但有返回值。
                System.out.println("call methond begin!");
                Thread.sleep(1000*10);
                System.out.println("call methond over!");
                int a = 100;
                int b = 200;
                return a+b;
            }
        });
        Thread t = new Thread(task);
        t.start();
        try {
            //获取返回结果
            Object o = task.get();//这里主线程main会受阻塞。因为要等待执行完get()方法,而get方法需要
                                    // 等待t线程结束获取结果,之后main线程才继续往下执行
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("hello world");
    }
}
/*
call methond begin!
call methond over!
300
hello world
*/

13.关于Object类中的wait和notify方法(生产者和消费者模式)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hG4rhBHT-1602401030310)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928211916809.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FbZPQyp3-1602401030312)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928212052271.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rl2tC0lU-1602401030313)(C:\Users\23236\AppData\Roaming\Typora\typora-user-images\image-20200928212523425.png)]

package com.codesing.Product_Consumme;
/*
* 使用wait方法和notify方法实现生产者模式-消费者模式
*       1.生产线程负责生产,消费线程负责消费,生产线程和消费线程达到均衡。
*       2.wait和notify方法每个java对象都有,不是线程对象的方法
*       3.wait和notify方法建立在线程同步的基础上。多线程共享一个仓库,存在线程安全问题。
*       4.wait方法作用:o.wait()让正在o对象上运行的t线程进入等待状态,并且释放t线程之前所占有的o对象的锁。
*       5.notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。
* 模拟:
* list集合代表仓库,1个就表示满了,0个表示为空
*一个消费者线程,一个生产者线程
* */
import com.sun.javafx.collections.ListListenerHelper;

import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList();

        Thread product = new Thread(new Product(list));
        Thread consummer = new Thread(new Consummer(list));
        product.setName("生产者模式");
        consummer.setName("消费者模式");

        product.start();
        consummer.start();
    }
}
//生产者
class Product implements Runnable{
    private List list;

    public Product(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        //一直生产
        while (true){
            //给仓库对象list加锁
            synchronized (list){
                if (list.size() > 0){//仓库满了
                    try {
                        list.wait();//当前线程进入等待状态,释放list集合的锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //执行到这里,消费完了,仓库为空,开始生产
                Object o = new Object();
                list.add(o);
                System.out.println(Thread.currentThread().getName()+"--生产--->"+o);
                //生产完成,唤醒消费者消费
                list.notify();
            }
        }
    }
}

//消费者
class Consummer implements Runnable{
    private List list;

    public Consummer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        //一直消费
        while (true){
            synchronized (list){
                if (list.size() == 0){//仓库空了
                    try {
                        list.wait();//当前线程进入等待状态,释放list集合锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //执行到这里,仓库满了,开始消费
                Object remove = list.remove(0);
                System.out.println(Thread.currentThread().getName()+"--消费--->"+remove);
                //消费完了,唤醒生产者生产
                list.notify();
            }
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值