java多线程知识

目录

1.Thread下的方法

Thread.currentThread()

t.join() 

t.interrupt()/Thread.interrupted()

Thread.yield() 

2.volatile

volatile和synchronized的区别

3. synchronized

用法

作用

wait()/notify()

面试wait和sleep的区别 

总结


1.Thread下的方法

Thread.currentThread()

可以获取当前的线程,拿到这个后可以获取线程的一些信息,例如名字,状态,id

public class Test2 {
    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            System.out.println("你好 "+Thread.currentThread().getState());
        });
        t.start();
        System.out.println(t.getName());
        System.out.println(Thread.currentThread().getName());
    }
}

运行结果:

 

t.join() 

join的意思是连接合并,所以这里可以看作是合并执行流,下面的例子就是主线程等待子线程,合并成一个线程。

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() ->{
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("你好 "+Thread.currentThread().getState());
        });
        t.start();
       
       //会一直的阻塞等待,直到子线程结束
        t.join();

        System.out.println("我是主线程,已经等了5秒了~~");
    }
}

重载形式:t.join(1000),里面的单位是毫秒,所以这里表示等待一秒之后就不等待了 ,而是也执行主线程,但是如果主线程执行完毕后,子线程没执行完毕也会继续的执行,直到结束。

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() ->{
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("你好 "+Thread.currentThread().getState());
        });
        t.start();
        t.join(1000);
        System.out.println("等了一秒不等了");
        System.out.println("主线程结束");
    }
}

运行结果:

 

t.interrupt()/Thread.interrupted()

对于interrupt(),只是给t线程发了消息(给线程t设置了停止标值,但是实际不会影响t的执行),前提是t不处于阻塞状态,如果此时t处于阻塞的状态则会以异常的形式通知t。

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() ->{
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("你好 "+Thread.currentThread().getState());
        });
        t.start();
        //先睡完一秒后,线程t一定处于睡眠状态
        Thread.sleep(1000);
        t.interrupt();
        System.out.println("主线程结束");
    }
}

运行结果:捕获异常后继续执行后面的代码。

 

对于Thread.interrupted(),用于检测当前的线程是否被终止,这个就是完全有我们自己控制了。如果我们通过上述的方法,检测到希望被终止了,我们可以选择继续做其他的事情,也可以停止,这在于我们自己写的代码。 

Thread.yield() 

作用:让出CPU,线程从运行->就绪(随时可以被调度)

2.volatile

对于volatile是修饰的变量,作用可以保护变量的内存可见性、保护代码重排序

1.volatile保证变量对所有线程的可见性:当volatile变量被修改,新值对所有线程会立即更新。或者理解为多线程环境下使用volatile修饰的变量的值一定是最新的。

2.jdk1.5以后volatile完全避免了指令重排优化,实现了有序性。

原理:

获取JIT(即时Java编译器,把字节码解释为机器语言发送给处理器)的汇编代码,发现volatile多加了lock addl指令,这个操作相当于一个内存屏障,使得lock指令后的指令不能重排序到内存屏障前的位置。这也是为什么JDK1.5以后可以使用双锁检测实现单例模式。

lock前缀的另一层意义是使得本线程工作内存中的volatile变量值立即写入到主内存中,并且使得其他线程共享的该volatile变量无效化,这样其他线程必须重新从主内存中读取变量值。

volatile和synchronized的区别

1.volatile 本质是在告诉 JVM 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

2.volatile 仅能使用在变量级别;synchronized 则可以使用在 变量. 方法. 和类级别。

3.volatile 仅能实现变量的修改可见性,不能保证原子性;而synchronized 则可以 保证变量的修改可见性和原子性

4.volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞

5.volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。

3. synchronized

用法

1.修饰普通方法:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁

2.修饰静态方法:作用于当前类,进入同步代码前要获得当前类对象的锁,synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁

3.修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁

作用

1.原子性:确保线程互斥的访问同步代码;

2.可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;

3.有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”。

wait()/notify()

对于wait()和notify()方法,都是Object类中的方法,所以Java中的对象都有这两个方法。如果要使用这两个方法,必须对对象进行加synchronized锁

代码演示:

public class Test2 {
    static class MyThread extends Thread {
        Object o;
        public MyThread(Object o){
            this.o = o;
        }
        @Override
        public void run() {
            synchronized (o){
               try {
                   o.wait();   //1.释放o锁  2.阻塞等待被notify  3.再次加锁
                   System.out.println("子线程被唤醒了");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        }
    }

    public static void main(String[] args) throws InterruptedException {
         Object o = new Object();
        //确保是同一个对象
        MyThread myThread = new MyThread(o);
        myThread.start();

        System.out.println("两秒后唤醒子线程");
        Thread.sleep(2000);

        synchronized (o){
            o.notify();
        }
    }
}

注意:wait-notify是没有状态的,就是如果先notify然后在wait,wait是无法感知的,会永远等下去。 

面试wait和sleep的区别 

1.sleep方法:是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,等待CPU的到来。睡眠不释放锁(如果有的话)。

2.wait方法:是Object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。

3.sleep 方法没有释放锁;而 wait 方法释放了锁 。

4.sleep 通常被用于暂停执行;wait 通常被用于线程间交互/通信

5.sleep() 方法执行完成后,线程会自动苏醒。wait的重载可以使用 wait(long timeout)超时后线程会自动苏醒。wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。


总结

加油哦~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值