手撕多线程基础篇

1.java中多线程
一个Java应用程序java.exe,其实至少三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
2.创建多线程的几种方式
Talk is cheap! Show me your code !
继承Thread类的方式

* A <i>thread</i> is a thread of execution in a program. The Java
* Virtual Machine allows an application to have multiple threads of
* execution running concurrently.
public class ThreadTest1 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("这是创建出来的线程");
    }
}

///////////主程序///////////////
public class MutiThread {
    public static void main(String[] args) {

        ThreadTest1 test1 = new ThreadTest1();
        test1.setPriority(Thread.MIN_PRIORITY);
        test1.start();
        System.out.println("这是主线程");
    }
}
运行结果:
注意:继承Thread类创建出的线程,在实例化后启动时必须使用start()方法才能有多线程效果,使用run()方法仍然是单线程执行,因为调用run()方法,仍然属于单线程基本行为。

②实现Runnable接口的方式
The <code>Runnable</code> interface should be implemented by any
* class whose instances are intended to be executed by a thread. The
* class must define a method of no arguments called <code>run</code>.
public class RunnableTest implements Runnable {
    @Override
    public void run() {

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("这是创建出来的线程");
    }
}
////////////主程序/////////////
public class MutiThread {

    public static void main(String[] args) {
        Thread thread = new Thread(new RunnableTest());
        thread.start();
        System.out.println("这是主线程");
    }
}
运行结果:

以上两种方式对比,优先选用实现Runnable接口,原因
一是能够避免单继承的局限性,二是能够更加合适处理多线程资源共享的情况
③jdk5.0新增实现Callable接口方式
* A task that returns a result and may throw an exception.
* Implementors define a single method with no arguments called
* {@code call}.
public class CallableTest implements Callable {
    @Override
    public Integer call() throws Exception {

        Thread.sleep(3000);
        System.out.println("这是创建的线程");
        return 0;
    }
}

////////////////////////主程序////////////////////
public class MutiThread {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建Callable接口实现类的对象
        CallableTest callableTest = new CallableTest();
        //将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableTest);
        //将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        Thread thread = new Thread(futureTask);

        thread.start();

        //获取Callable中call方法的返回值
        //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
//        System.out.println(futureTask.get());
        System.out.println("这是主线程");
    }
}
注意:这里有一个细节,倘若我们不需要返回结果,那么上述执行与前两种并没有什么区别。但是如果我们需要返回值,那么在执行到返回值位置时,线程阻塞后面的主线程必须等待
返回结果线程执行完才能继续往后执行。
④通过线程池的方式创建
public class MutiThread {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15)  核心池的大小
//        service1.setKeepAliveTime();  线程没任务时最多保持多长时间后会终止


        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new RunnableTest());//适合适用于Runnable
//        service.execute(new RunnableTest());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }
}
3.线程的生命周期

4.线程的同步机制   ---------------------->在java中,我们通过线程的同步机制,来解决线程的安全问题,主要针对共享数据的安全问题
①同步代码块                  
        synchronized (this){
            
        }
这里this就是同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁。 按照常用的锁分成两种  :this 实例化对象锁     Person.class   当前类本身
tip:在实现runnable接口创建多线程的方式中,可以考虑使用this作为同步监视器。而在继承Thead类创建多线程时,慎用this同步监视器,考虑使用类本身作为同步监视器
②同步方法
    public synchronized void test(){
        
    }
    
    public static synchronized void test1(){
        
    }
分成静态方法和非静态方法,静态方法使用的是类本身作为同步监视器,非静态方法使用this作为同步监视器
③lock 锁方式
    private Lock lock = new ReentrantLock();
    
    public void testlock(){
        //加锁
        lock.lock();

        try {
            
            //方法执行
            System.out.println("执行代码");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
lock方式与synchronized相比,lock锁必须手动释放锁,而synchronized自动释放锁

使用同步机制让懒汉式变成线程安全
class Bank{

    private Bank(){}

    private static Bank instance = null;

    public static Bank getInstance(){
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if(instance == null){
//
//                instance = new Bank();
//            }
//            return instance;
//        }
        //方式二:效率更高   双端检测机制
        //一个线程第一次读取instance
        if(instance == null){
            
            synchronized (Bank.class) {
                //该线程第二次读取instance
                if(instance == null){

                    instance = new Bank();
                }
            }
        }
        return instance;
    }

}
5.线程的死锁现象
简单的解释,有两个线程在执行,这两个线程在各自的同步方法块中执行且各自又握有对方想要的资源,双方都在等待对方释放锁,最后形成了死锁
public static void main(String[] args) {

    StringBuffer s1 = new StringBuffer();
    StringBuffer s2 = new StringBuffer();
    
    new Thread(){
        @Override
        public void run() {

            synchronized (s1){

                s1.append("a");
                s2.append("1");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (s2){
                    s1.append("b");
                    s2.append("2");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }
    }.start();


    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (s2){

                s1.append("c");
                s2.append("3");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (s1){
                    s1.append("d");
                    s2.append("4");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }
    }).start();


}
6.线程间的通信
基本概念:
 ①.线程通信涉及到的三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
说明:
wait()notify()notifyAll()三个方法必须使用在同步代码块或同步方法中。
wait()notify()notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
否则,会出现IllegalMonitorStateException异常
wait()notify()notifyAll()三个方法是定义在java.lang.Object类中。
面试题:sleep() 和 wait()的异同?
1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2.不同点:1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
         2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
         3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
经典生产者与消费者模型
public class ProducerAndConsumer {

    public static void main(String[] args) {
        Alter alter = new Alter();

        Thread produce = new Thread(new Producer(alter));
        Thread consumer = new Thread(new Consumer(alter));

        produce.start();
        consumer.start();
    }
}


class Alter{

    private  int count = 0;

    //生产商品服务
    public  void produce(){
        synchronized (this) {
            if (count < 20) {
                count++;
                System.out.println("生产者 :第" + count);
                notifyAll();
            } else {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //消费商品服务
    public synchronized void consume(){
        
            if(count > 0){
                System.out.println("消费者消费第 :" + count);
                count--;
                notifyAll();
            }else{

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

}


//生产厂家
class Producer implements Runnable{

    public  Alter alter;

    public Producer(Alter alter) {
        this.alter = alter;
    }

    @Override
    public void run() {
        while(true) {
            alter.produce();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//消费者
class Consumer implements Runnable{

    public  Alter alter;

    public Consumer(Alter alter) {

        this.alter = alter;
    }

    @Override
    public void run() {
        while(true) {
            alter.consume();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}





posted @ 2020-10-21 09:12  大洋游侠1  阅读(134)  评论(0)    收藏  举报