java多线程

  1. java多线程
    1. 别人总结的:

此文档为java多线程的学习总结

java多线程

Java中的多线程你只要看这一篇就够了

1583507126

这张图的几点解释:

1.thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

2.sleep()与wait()。

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

获取对象锁进入运行状态。

别人总结的:

是否释放锁:调用sleep和yield的时候不释放当前线程所获得的锁,但是调用await/wait的时候却释放了其获取的锁并阻塞等待。

调用后何时恢复:

sleep让线程阻塞,且在指定的时间之内都不会执行,时间到了之后恢复到就绪状态,也不一定被立即调度执行;

yield只是让当前对象回到就绪状态,还是有可能马上被再次被调用执行。

await/wait,它会一直阻塞在条件队列之上,之后某个线程调用对应的notify/signal方法,才会使得await/wait的线程回到就绪状态,也是不一定立即执行。

谁的方法:yield和sleep方法都是Thread类的,而wait方法是Object类的,await方法是Condition显示条件队列的。

执行环境:yield和sleep方法可以放在线程中的任意位置,而await/wait方法必须放在同步块里面,否则会产生运行时异常。

1.多线程中线程安全:线程同步与锁定synchronized

/**
 * FileName: WebDemo
 * Author:   braincao
 * Date:     2018/10/2 10:59
 * Description: 模拟抢票过程,主要演示线程不安全与加锁后安全
 */

public class WebDemo implements Runnable{
    private int num = 20; //总共20张票
    private boolean flag = true;

    public static void main(String[] args){
        //真实角色
        WebDemo w = new WebDemo();
        //代理角色
        Thread t1 = new Thread(w, "路人");
        Thread t2 = new Thread(w, "黄牛");
        Thread t3 = new Thread(w, "程序员");
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }

    public void run(){
        while(flag) {
//            test01(); //线程不安全
//            test02(); //线程安全
            test03(); //线程安全
        }
    }

    //线程不安全
    public void test01(){
        if(num<=0){
            flag = false;
            return;
        }
        try{
            Thread.sleep(500); //模拟抢票延时
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "抢到了" + num--);
    }

    //线程安全,synchronized同步方法
    public synchronized void test02(){
        if(num<=0){
            flag = false;
            return;
        }
        try{
            Thread.sleep(100); //模拟抢票延时
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "抢到了" + num--);
    }

    //线程安全,synchronized同步块
    public void test03(){
        synchronized (this){
            if(num<=0){
                flag = false;
                return;
            }
            try{
                Thread.sleep(100); //模拟抢票延时
            }
            catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "抢到了" + num--);
        }
    }
}    

out:

//线程不安全test01
黄牛抢到了20
路人抢到了19
程序员抢到了18
路人抢到了17
程序员抢到了15
黄牛抢到了16
路人抢到了14
程序员抢到了13
黄牛抢到了12
黄牛抢到了11
路人抢到了9
程序员抢到了10
路人抢到了8-->重复
程序员抢到了7
黄牛抢到了8-->重复
路人抢到了5
黄牛抢到了4
程序员抢到了6
黄牛抢到了3
路人抢到了1
程序员抢到了2

//线程安全test02、test03
路人抢到了20
路人抢到了19
路人抢到了18
路人抢到了17
程序员抢到了16
程序员抢到了15
程序员抢到了14
程序员抢到了13
程序员抢到了12
程序员抢到了11
程序员抢到了10
程序员抢到了9
程序员抢到了8
程序员抢到了7
程序员抢到了6
程序员抢到了5
程序员抢到了4
程序员抢到了3
程序员抢到了2
程序员抢到了1

###2、多线程中的单例模式(加同步锁)

  • 单例模式创建的方式

1.懒汉式:

1)、构造器私有化,避免外部直接创建对象

2)、声明一个私有的静态变量

3)、创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象。且考虑多线程因素,这里要使用double checking的同步锁

懒汉式单例模式模板:

/**
 * FileName: Lanhan
 * Author:   braincao
 * Date:     2018/10/2 15:44
 * Description: 单例模式之懒汉式
 * 1、构造器私有化,避免外部直接创建对象
 * 2、声明一个私有的静态变量
 * 3、创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象。且考虑多线程因素,这里要使用double checking的同步锁
 */

public class Lanhan {
    private static Lanhan instance = null;
    private Lanhan(){

    }

    public static Lanhan getInstance(){
        if(Lanhan==null){ //double checking 这里是提高效率作用
            synchronized(Lanhan.class){ //同步块synchronized()中的参数是引用类型,但这里没有this对象,注意要用Lanhan.class
                if(Lanhan==null){//double checking 这里是安全同步作用
                    instance = new Lanhan();
                }
            }
        }
        return instance;
    }
}

饿汉式单例模式模板:

public class Lanhan {
    private static class JvmHolder{ //内部类,调用时再创建对象,而不是加载类时就创建对象,更有效率
        private static Lanhan instance = new Lanhan();
    }
    private Lanhan(){

    }

    public static Lanhan getInstance(){
        return JvmHolder.instance;
    }
}


一个测试例子:

/**
 * 单例设计模式:确保一个类只有一个对象
 * 多线程中单例模式需要添加同步锁
 */
class WebDemo{
    public static void main(String[] args){
//        Jvm jvm1 = Jvm.getInstance();
//        Jvm jvm2 = Jvm.getInstance();
//        System.out.println(jvm1);
//        System.out.println(jvm2); //单线程中,两者相等

        JvmThread t1 = new JvmThread(100);
        JvmThread t2 = new JvmThread(500);
        t1.start();
        t2.start();
        //out1:Thread-0-->创建:Jvm@46543a6c  Thread-1-->创建:Jvm@59dafc71 //多线程中,t1,t2不相等。这时需要添加同步锁
        //out2:Thread-0-->创建:Jvm@6ab31cd3  Thread-1-->创建:Jvm@6ab31cd3 //加同步锁后,多线程中t1、t2相等

    }
}

//多线程
class JvmThread extends Thread{
    private long time;
    public JvmThread(){

    }
    public JvmThread(long time){
        this.time = time;
    }

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + "-->创建:" + Jvm.getInstance(time));
    }
}

/**
 * 懒汉式
 * 1、构造器私有化,避免外部直接创建对象
 * 2、声明一个私有的静态变量
 * 3、创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
 */
class Jvm{
    private static Jvm instance = null; //懒汉式就是这里懒的创建对象

    private Jvm(){

    }

    //在这里添加同步锁后,多线程模式下也一样是单例模式了
public static Lanhan getInstance(){
    if(Lanhan==null){ //double checking 这里是提高效率作用
        synchronized(Lanhan.class){ //同步块synchronized()中的参数是引用类型,但这里没有this对象,注意要用Lanhan.class
            if(Lanhan==null){//double checking 这里是安全同步作用
                instance = new Lanhan();
            }
        }
    }
    return instance;
    }
}

3、死锁

过多的同步容易造成死锁,解决方法:生产者消费者模式

死锁例子演示:

public class Demo implements Runnable{
    private boolean flag = false;
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    public Demo(boolean flag){
        this.flag = flag;
    }
    public void run(){
        if(flag==true){
            synchronized (o1){
                System.out.println("t1 : o1");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("t2 : o1");
                }
            }
        }
        else{
            synchronized (o2){
                System.out.println("t2 : o2");
                synchronized (o1){
                    System.out.println("t2 : o2");
                }
            }

        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Demo(true));
        Thread t2 = new Thread(new Demo(false));
        t1.start();
        t2.start();
        //out:
        //t1:o1
        //t2:o2...
    }
}

4、生产者消费者模式:信号灯法

wait():等待、释放锁;sleep():等待、不释放锁

notify()/notifyAll():唤醒

wait()与notify()/notifyAll()是要与锁synchronized一起使用的

/**
 * FileName: App
 * Author:   braincao
 * Date:     2018/10/2 16:13
 * Description:生产者消费者模式:信号灯法
 */

public class App{
    public static void main(String[] args){
        //共同资源
        Movie m = new Movie();
        //两个线程
        Player p = new Player(m);
        Watcher w = new Watcher(m);
        new Thread(p).start();
        new Thread(w).start();
    }
}

/**
 * 一个场景,共同资源
 * 且使用生产者消费者模式:信号灯法
 * wait():等待、释放锁;sleep():等待、不释放锁
 * notify()/notifyAll():唤醒
 * wait()与notify()/notifyAll()是要与锁synchronized一起使用的
 *
 */
class Movie {
    private String pic;
    private boolean flag;
    /**
     * 信号灯
     * flag-->T 生产者生产,消费者等待,生产完成后通知消费者
     * flag-->F 消费者消费,生产者等待,消费完成后通知生产者
     */

    //生产
    public synchronized void player(String pic){
        if(!flag){//生产者等待
            try{
                this.wait(); //等待,释放锁
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //开始生产
        try{
            Thread.sleep(500);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //生产完毕
        System.out.println("生产了:" + pic);
        this.pic = pic;
        //通知消费者,唤醒正在等待的线程,这里只有一个线程在等待notifyAll()也可以
        this.notify();
        //生产者停下
        this.flag = false;
    }

    //观看
    public synchronized void watch(){
        if(flag){//消费者等待
            try{
                this.wait(); //等待,释放锁
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //开始消费
        try{
            Thread.sleep(200);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //消费完毕
        System.out.println("消费了:" + pic);
        //通知生产者,唤醒正在等待的线程,这里只有一个线程在等待notifyAll()也可以
        this.notify();
        //消费者停下
        this.flag = true;
    }
}

/**
 * 生产者
 */
class Player implements Runnable{
    private Movie m;

    public Player(Movie m){
        this.m = m;
    }
    public void run(){
        for(int i=0; i<20; ++i){
            if(i%2==0){
                m.player("左青龙");
            }
            else{
                m.player("右白虎");
            }
        }
    }
}

/**
 * 消费者
 */
class Watcher implements Runnable{
    private Movie m;

    public Watcher(Movie m){
        this.m = m;
    }
    public void run(){
        for(int i=0; i<20; ++i){
            m.watch();
        }
    }
}

out:

消费了:null
生产了:左青龙
消费了:左青龙
生产了:右白虎
消费了:右白虎
生产了:左青龙
消费了:左青龙
生产了:右白虎
消费了:右白虎
生产了:左青龙
消费了:左青龙
生产了:右白虎
消费了:右白虎
生产了:左青龙

5、线程之任务调度

想让该程序晚上11点跑,使用线程之任务调度,Timer定时器类,相当于一个闹钟。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class TimerDemo {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("so easy...");
            }
        }, new Date(System.currentTimeMillis()+3000), 1000); //3秒后运行,每隔1秒运行一次; 如果没有period参数,就只运行一次
    }
}

out:

so easy...
so easy...
so easy...
so easy...

欢迎转载,欢迎错误指正与技术交流,欢迎交友谈心

文章标题:java多线程

文章字数:2.7k

本文作者:Brain Cao

发布时间:2018-12-16, 15:54:20

最后更新:2020-03-06, 23:25:51

原始链接:https://braincao.cn/2018/12/16/java-multi-thread/

版权声明:本文为博主原创文章,遵循 BY-NC-SA 4.0 版权协议,转载请保留原文链接与作者。

目录
×

喜欢请收藏,疼爱就打赏