查漏补缺之——Java多线程

复习面试题中遇到锁的内容当时大一学习的时候感觉懵懂,现在重新复习一下。

1.1多线程

  1.1.1线程

    1.什么是线程

    线程是程序执行的一条路径,一个进程中包含多条进程

    2.并行与并发

    并行是两个任务同时运行,甲任务进行的同时,乙任务也在进行

    并发是指两个任务都请求运行,处理器只能处理一个任务,就将两个任务进行轮流进行

       3.Java程序运行原理和JVM的启动是否为多线程

    A:Java程序运行原理

      Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程,该进程为主线程,主线程去调用某个main方法

    B:JVM的启动是多线程的吗

      JVM的启动至少启动了垃圾回收线程和主线程,所以是多线程的

       

    证明JVM运行是多线程

package com.littlepage.test2;

public class Demo {
    @Override
    protected void finalize(){
        System.out.println("rubbish is cleaned");
    }
    public static void main(String[] args) {
        for(int i=0;i<1000000;i++){
            new Demo();
        }
        for(int i=0;i<10000;i++){
            System.out.println("main is doing");
        }
    }
}

 

 

  1.1.1实现方式

    1.继承Thread类

public class TreadDemo01 {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        mt.start();
        for(int i=0;i<1000;i++){
            System.out.println("Main method");
        }
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            System.out.println("My Thread is running");
        }
    }
}

    2.实现runnable接口

package com.littlepage.test2;

public class ThreadDemo02 {
    public static void main(String[] args) {
        MyRunnable mr=new MyRunnable();
        new Thread(mr).start();
        for(int i=0;i<1000;i++){
            System.out.println("Main method");
        }
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            System.out.println("My Runnable is running");
        }
    }
}

    3.何时使用继承何时使用接口

      一般情况下使用继承的多线程,因为代码简单,接口线程在一个类有了父类的时候,使用接口

    4.匿名内部类进行多线程

package com.littlepage.test2;

public class ThreadDemo03 {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                for(int i=0;i<1000;i++){
                    System.out.println("My Thread is running");
                }
            }
        }.start();
        for(int i=0;i<1000;i++){
            System.out.println("Main method");
        }
    }
}
package com.littlepage.test2;

public class ThreadDemo03 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<1000;i++){
                    System.out.println("My Runnable is running");
                }
            }
        }).start();
        for(int i=0;i<1000;i++){
            System.out.println("Main method");
        }
    }
}

1.2获取线程名和设置线程名

   1.1获取线程名字(线程的默认名字为Thread-0 Thread-1....)

package com.littlepage.test2;

public class Demo04GetName {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println(getName());
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                System.out.println(getName());
            }
        }.start();
    }
}
package com.littlepage.test2;

public class Demo04GetName {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();;
    }
}

 

    1.2设置线程名

package com.littlepage.test2;

public class Demo04GetName {
    public static void main(String[] args) {
        Thread t1=new Thread(){
            @Override
            public void run() {
                System.out.println(getName());
            }
        };
        t1.setName("线程0");
        Thread t2=new Thread(){
            @Override
            public void run() {
                System.out.println(getName());
            }
        };
        t2.setName("线程1");
        
        t1.start();t2.start();
    }
}

1.3休眠线程

   1.1简介休眠方法

    Thread.sleep(long millis) 休眠millis毫秒

    Thread.sleep(long millis,int nanosecond) 休眠millis毫秒和nanosecond纳秒

   举例:省略

1.4守护线程(Daemon)

  守护线程就是我们所说的后台程序,非守护线程结束,守护线程将会立即结束

  举例:

package com.littlepage.test2;

public class DemoDaemon {
    public static void main(String[] args) {
        Thread a=new Thread(){
            @Override
            public void run() {
                for(int i=0;i<10000;i++){
                    System.out.println(getName()+"\t"+i);
                }
            }
        };
        Thread b=new Thread(){
            @Override
            public void run() {
                for(int i=0;i<2;i++){
                    System.out.println(getName()+"\t"+i);
                }
            }
        };
        a.setDaemon(true);
        a.start();
        b.start();
    }
}

  线程2结束,守护线程1也立即结束

1.5加入线程

  加入线程join() 当前线程暂停,等待指定的线程执行结束后,再进行执行

  举例:

package com.littlepage.test2;

public class DemoDaemon {
    public static void main(String[] args) {
        final Thread a=new Thread(){
            @Override
            public void run() {
                for(int i=0;i<10000;i++){
                    System.out.println(getName()+"\t"+i);
                }
            }
        };
        Thread b=new Thread(){
            @Override
            public void run() {
                for(int i=0;i<2;i++){
                    if(i==1)
                        try {
                            a.join();//等a线程执行完再执行完,再执行b
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    System.out.println(getName()+"\t"+i);
                }
            }
        };
        a.setDaemon(true);
        a.start();
        b.start();
    }
}

 

1.4同步代码块

  1.4.1什么情况下需要同步

    多线程并发,多段代码同时执行,我们希望某一段代码执行过程中CPU不要切换到其他线程来工作,这时候我们需要同步

    代码示例:

package com.littlepage.test2;

public class DemoSynchronized {
    public static void main(String[] args) {
        final Printer p=new Printer();
        new Thread(){
            public void run(){
                while(true) p.print1();
            }
        }.start();
        new Thread(){
            public void run(){
                while(true) p.print2();
            }
        }.start();
    }
}
class Printer{
    public void print1(){
        System.out.print("A");
        System.out.print("P");
        System.out.print("P");
        System.out.print("l");
        System.out.println("e");
    }
    public void print2(){
        System.out.print("B");
        System.out.print("a");
        System.out.print("n");
        System.out.print("a");
        System.out.print("n");
        System.out.println("a");
    }
}

  这段代码在运行过程中出现了以下情况

  

  原因是,打印过程中进行了切换导致,所以我们需要一个同步锁

package com.littlepage.test2;

public class DemoSynchronized {
    public static void main(String[] args) {
        final Printer p=new Printer();
        new Thread(){
            public void run(){
                while(true) p.print1();
            }
        }.start();
        new Thread(){
            public void run(){
                while(true) p.print2();
            }
        }.start();
    }
}
class Printer{
    Object lock=new Object();
    public void print1(){
        synchronized (lock) {
            System.out.print("A");
            System.out.print("P");
            System.out.print("P");
            System.out.print("l");
            System.out.println("e");
        }
    }
    public void print2(){
        synchronized (lock) {
            System.out.print("B");
            System.out.print("a");
            System.out.print("n");
            System.out.print("a");
            System.out.print("n");
            System.out.println("a");
        }
    }
}

    锁可以是任何一个对象,这边直接new一个Object,但是在两个方法内部,必须用一个lock

1.5同步方法

package com.littlepage.test2;

public class DemoSynchronized {
    public static void main(String[] args) {
        final Printer p=new Printer();
        new Thread(){
            public void run(){
                while(true) p.print1();
            }
        }.start();
        new Thread(){
            public void run(){
                while(true) p.print2();
            }
        }.start();
    }
}
class Printer{
    public synchronized void print1(){
        System.out.print("A");
        System.out.print("P");
        System.out.print("P");
        System.out.print("l");
        System.out.println("e");
    }
    public synchronized void print2(){
        System.out.print("B");
        System.out.print("a");
        System.out.print("n");
        System.out.print("a");
        System.out.print("n");
        System.out.println("a");
    }
}

    同步方法的锁是this, 所以我们有个想法, 包括同步代码块和同步代码,我们所有的锁都可以用this关键字, 不用Object造成冗余

    静态方法的锁是字节码对象

1.6线程的安全问题

  1.6.1书本的售票例子

package com.littlepage.test2;

public class DemoTicket {
    public static void main(String[] args) {
        new TicketSeller("窗口1").start();
        new TicketSeller("窗口2").start();
        new TicketSeller("窗口3").start();
        new TicketSeller("窗口4").start();
    }
}
class TicketSeller extends Thread{
    private static int tickets=100;
    
    public TicketSeller(String windowName) {
        super(windowName);
    }
    
    public  void run(){
        while(true){
            if(tickets<=0){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getName()+"......"+tickets--);
        }
    }
}

   结果出现了重复票和负票

    

  解决方式,加上同步代码块

package com.littlepage.test2;

public class DemoTicket {
    public static void main(String[] args) {
        new TicketSeller("窗口1").start();
        new TicketSeller("窗口2").start();
        new TicketSeller("窗口3").start();
        new TicketSeller("窗口4").start();
    }
}
class TicketSeller extends Thread{
    private static int tickets=100;
    
    public TicketSeller(String windowName) {
        super(windowName);
    }
    
    public  void run(){
        synchronized (TicketSeller.class) {
            while(true){
                if(tickets<=0){
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+"......"+tickets--);
            }
        }
    }
}

  使用Runnable实现

1.7死锁

  

posted @ 2019-04-07 14:54  SteveYu  阅读(236)  评论(0编辑  收藏  举报