Java高并发effective

用户线程(User Thread)和守护线程(Daemon Thread)

Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

值得一提的是,守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。下面的方法就是用来设置守护线程的。 Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
Thread daemonTread = new Thread();

// 设定 daemonThread 为 守护线程,default false(非守护线程)
daemonThread.setDaemon(true);

// 验证当前线程是否为守护线程,返回 true 则为守护线程
daemonThread.isDaemon();
jps展示当前jvm内所有的进程信息(进程号,进程名字)
jconsole +进程号 (查看进程的状态)
在这里插入图片描述
thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程

互斥和同步的区别

我的理解:互斥就是线程之间对一个资源的竞争,执行的结果是无序的,同步是要求程序员把这个资源进行有序化的操作(即这个对象哪个时候哪个线程来执行),就是按程序员的逻辑进行有序的该资源进行访问。

互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
同步其实已经实现了互斥,所以同步是一种更为复杂的互斥。
互斥是一种特殊的同步。
互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。

线程什么时候终止

当线程的run()方法结束线程也就自然死亡了,也可以调用.stop()强制让线程结束

调用start()方法时至少存在两个线程,一个是调用你自己的线程,例如:main线程,还有一个是自己创建的线程来执行run()方法

wait()和notify()函数的深入理解,涉及到的线程之间的传数据

public class Thread_Wait_Notify_  implements Runnable{

    private String name;
    private Object pre;
    private Object self;

    public Thread_Wait_Notify_(String name, Object pre, Object self) {
        this.name = name;
        this.pre = pre;
        this.self = self;
    }
    @Override
    public void run() {

        int count=10;
        while (count>0){
            synchronized (pre){
                synchronized (self){
                    self.notify();
                    System.out.print(name);
                    count--;
                }
                try {
                    pre.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }

    }
}
 public static void main(String[] strings) throws InterruptedException {
        Object a=new Object();
        Object b=new Object();
        Object c=new Object();
        new Thread(){
           @Override
           public void run(){
           }
        };//不启动

        Thread_Wait_Notify_ pa=new Thread_Wait_Notify_("A",c,a);
        Thread_Wait_Notify_ pb=new Thread_Wait_Notify_("B",a,b);
        Thread_Wait_Notify_ pc=new Thread_Wait_Notify_("C",b,c);
        new Thread(pa,"A").start();
        //Thread.sleep(100);//确保按ABC这个顺序执行
        new Thread(pb,"B").start();
        //Thread.sleep(100);
        new Thread(pc,"C").start();
       // Thread.sleep(100);
    }

输出:
ABCABCABCABCABCABCABCABCABCABC

object.wait():这个对象在那个线程里面调用了,那个线程就进入停止状态,不再执行,这就是为什么不连续执行输出10个A的原因,代码中线程A的pre.wait()执行了,其实就是c对象.wait()(虽然c对象是在主线程传过来的,不属于A线程创建)那么线程A就进入停止状态,不会往下执行,然后B线程又被主线程调用了,进入B线程执行,打印了一个B,又执行了pre.wait(),B线程又进入停止状态,又调用了C线程进入执行,打印了一个C,执行self.notify(),其实也就是C对象.notify(),这句话很重要,这使得之前因为这个对象进入停止状态的线程重新向下执行。wait()方法会使得该对象的使用权释放出来给其他的线程使用,同时会使得执行这条语句的线程停止。。。

把run方法的代码改成如下

 public void run() {

        int count=10;
        while (count>0){
            synchronized (pre){
                synchronized (self){
                    System.out.print(name);
                    count--;
                    System.out.print(self);
                    System.out.println("==");
                    self.notify();
                }
                try {
                    pre.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }

    }

看输出:
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==
Ajava.lang.Object@7e437185==
Bjava.lang.Object@7042b8f1==
Cjava.lang.Object@3cd1a2f1==

上代码可以看出,一个对象在一个线程执行了.wait()方法后不用在另外一个线程先执行notify()才有这个对象的使用权,上一个线程执行了wait()就表明其他的线程可以使用这个对象。

最后补充一下:
这个程序不会停止:
在这里插入图片描述
还有B线程和C线程,因为最后是B线程和C线程内执行了pre.wait();
在这里插入图片描述
打印最后一轮ABC是这样子的:A线程拯救了B线程,自己锁住了,B拯救了C,自己锁住了,C拯救了A,自己锁住了,所以最后剩下B和C线程没有结束,两个都在等待别人把它notify();但是没有线程调用这个语句来唤醒它也就是永远的停在了那里
代码改进:怎么样子让程序正常的停止:
修改代码:
思路:在A线程被C线程唤醒之后,退出while循环后,执行self.notify(),(因为之前B线程执行了pre.wait(),因为a对象被停止了)唤醒之后,B线程就可以执行了,也是退出while循环后,执行b.notify(),(之前C线程因为b对象.wait()进入停止状态),C线程可以继续向下执行,最后B和C线程都正常结束,整个程序正常结束。

@Override
    public void run() {

        int count=10;
        while (count>0){
            synchronized (pre){
                synchronized (self){
                    System.out.print(name);
                    count--;
                    System.out.print(self);
                    System.out.println("==");
                    self.notify();
                }
                try {
                    pre.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
       synchronized (self){
            self.notify();
       }

    }

明确一点哪个线程因为哪一个对象进入了停止状态

notify()和wait()为什么一定要要放在同步synchronized{}关键字里面

操作系统层面:
每一个对象都有一个与之对应的监听器:这个监听器包括整个对象的锁,同步队列和等待队列
因为wait()方法进入暂停的线程进入等待队列,唤醒之后和其他线程竞争失败就进入同步队列;

wait()方法的语义有两个,一是释放当前对象锁,另一个是进入阻塞队列,可以看到,这些操作都是与监视器相关的,当然要指定一个监视器才能完成这个操作了

notify()方法也是一样的,用来唤醒一个线程,你要去唤醒,首先你得知道他在哪儿,所以必须先找到该对象,也就是获取该对象的锁,当获取到该对象的锁之后,才能去该对象的对应的等待队列去唤醒一个线程。值得注意的是,只有当执行唤醒工作的线程离开同步块,即释放锁之后,被唤醒线程才能去竞争锁。

比如说A线程执行了self.notify(),那么就会去找这个a对象的的锁,获取了这个锁之后找到a对象的等待队列,唤醒之前执行了pre.wait()的B线程,()如果有多个线程对a对象执行了wait()那么notifyAll()可以唤醒所有的线程

值得注意的是:虽然执行了.notify()操作,但是被唤醒的B线程并不会立即去竞争a对象,因为只有等synchronized{}函数块执行完了才会去执行的

synchronized实现同步的关键:

synchroned关键字可以对普通方法,静态方法和在括号内加一个对象这3种用法
上面的wait()和notify()函数都是用在第三种用法上的代码块

1.作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;

2.作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;

3.作用于代码块,这需要指定加锁的对象,对所给的指定对象加锁,进入同步代码前要获得指定对象的锁。

synchronized关键字作用在run()函数上面

public class Mutl_Thread implements Runnable {

    int count=0;
     //int self=0;//+"self:"+self++

    @Override
    public synchronized void run() {
        for (int i=0;i<3;i++){
            System.out.println(Thread.currentThread().getName()+"count:"+count++);
            System.out.println(Thread.currentThread().getName()+"=============");

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

    }
    public static void main(String[] arg) throws InterruptedException {
        Mutl_Thread mutl_thread=new Mutl_Thread();//这个是一个对象实例,
        // synchronized在run()方法上加了,run方法操作了count,实质上就是给这个实例加上了锁,其他的Thread()再访问这个实例就没办法了
        Mutl_Thread mutl_thread1=new Mutl_Thread();
        //下面两个Thread()线程都是共用用上面这个实例来进行测试的,run()不加synchronized就保证不了同步
        //如果分别用到了两个不同的对象实例,那就不是共享对象,不涉及同步

        Thread thread01=new Thread(mutl_thread,"THread01");
        Thread thread02=new Thread(mutl_thread,"Thread02");
        thread01.start();
        thread02.start();
      //  Thread.sleep(1000);
    }
}

输出
THread01count:0
THread01=============
THread01count:1
THread01=============
THread01count:2
THread01=============
Thread02count:3
Thread02=============
Thread02count:4
Thread02=============
Thread02count:5
Thread02=============

可以看出:一个线程获取该对象锁之后不会被其他的线程调用,这个应该不是处以
但是如果没有synchronized方法就无法实现同步,就是进入一个多线程的竞争的状态:每一次输出的结果都不一样
Thread02count:1
Thread02=============
Thread02count:2
Thread02=============
Thread02count:3
Thread02=============
THread01count:0
THread01=============
THread01count:4
THread01=============
THread01count:5
THread01=============

注意啊:因为wait()进入停止状态的线程会释放该线程所有对象的资源的锁,也就是说不仅仅是调用wait()方法的对象会被释放;sleep()函数会让线程进入一个睡眠的状态,但是不会释放对象的锁,也就是说必须等我睡完觉了再继续执行。sleep()函数谁调用谁就睡觉

更多的synchronized的底层原理参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值