java并发基础常用方法

1、线程和进程 

进程等于一个资源(即内存映射表)+多个指令执行序列,当切换进程的时候我们不仅要切换指令序列,而且要切换内存映射表,但是线程之间的切换只是在同一个内存映射表之间的指令切换(在java中切换pc,虚拟机栈,本地方法栈,不切换堆和方法区),线程既保留了并发的特点,又避免了线程切换的代价

 

2、start和run

调用start方法会新建一个线程,让这个新建的线程去调用run方法,而在当前线程中调用run方法,只是做为一个普通的方法调用,不能新建一个线程

 

3、怎么优雅的终止线程

不可用stop方法,因为stop方法太过暴力,强行把执行到一半的线程终止,可能会发生线程不一致的问题

可以用一个violatile变量来标记什么时候需要终止线程,让线程退出

 

4、wait和notify

当在一个对象实例上调用wait方法后,当前线程就会在这个对象上等待,比如线程A调用了obj.wait()方法,线程A就会进入等待状态,等待到别的线程调用了obj.notify方法为止,obj就成为了多个线程之间的有效通信手段。wait和notify究竟是怎么工作的呢,具体来说如果一个线程调用了object.wait(),那么它就会进入object对象的等待队列。当notify被调用时,它就会从这个等待队列中随机选择一个线程进行唤醒。这个选择是不公平的,并不是先等待的线程会优先被选择,完全随机。还需要强调的是,wait和notify并不能随便调用,它们必须包含在对应的synchronized语句中。相似的还有与ReentrantLock相关联的Condition的await方法,当线程被中断时,也能跳出等待。而且能被signal()方法唤醒,必须在相应的lock和unlock语句块中。Object.wait()和Thread.sleep()都可以让线程等待若干时间,除了wait可以被唤醒之外,wait释放目标对象的锁,但是sleep不会释放任何资源。

5、join和yield

public class JoinMain{

    public volatile static int i = 0;

    public static class AddThread extends Thread{
        
            public void run(){
                
                for(i = 0;i < 1000000;i++);

        }
 
    }

   public static void main(String[] args){

    AddThread at = new AddThread();
    at.start();
    at.join();

    System.out.println(i);

    }

}

join可以让调用线程在当前线程对象上进行等待,如果不调用join,主线程可能会输出0或者是得到的很小的数,因为AddThread还没开始执行,主线程就把i的值输出了,但使用join方法后,主线程就会等待AddThread执行完毕,并跟着AddThread一起走。

yield是指一旦调用,会使当前线程让出CPU,但让出CPU之后,还会进行CPU资源的争夺,能否再分配到就不一定了。

 

6、守护线程

守护线程是指在后台默默完成的一些系统性的服务,比如垃圾回收线程、JIT线程,与之相对应的是用户线程,用户进程就是系统的工作线程,当用户进程全部结束,这也意味着这个程序实际上无事可做了,当一个java应用内,只剩下守护线程时,Java虚拟机就会自然退出。比如下面的代码,当把t设置为守护线程时,当主线程结束后,程序就会自动结束,不然程序就会一直输出i am alive,需要注意的是setDaemon要设置在start()之前,不然t还是会被当成用户线程并且报异常IllegalThreadStateException

package JMM;

/**
 * @author: surepeng
 * @description:
 * @date: 2019/07/24
 **/
public class DaemonDemo {

    public static class DaemonT extends Thread {

        public void run() {

            while (true) {
                System.out.println("I am alive");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    public static void main(String[] args) throws InterruptedException {

        Thread t = new DaemonT();
        t.setDaemon(true);
        t.start();
        Thread.sleep(2000);
    }

}

 

7、synchronized

  关键词synchronized的作用是实现线程间的同步,它的工作是对同步的代码加锁,使得每一次只能有一个线程加入同步代码块,从而保证线程间的安全性

可以有多种用法:

指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。

直接用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。

直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

对同一个对象instance加锁:

public class AccountingSync implements Runnable{

       static AccountingSync instance = new AccountingSync();

       static int i = 0;

       public void run(){
        
            for(int j = 0;j<1000000;j++){

                synchronized(instance){

                    i++;
                }

            }
      }


    public static void main(String[] args) throws InterruptedException{

        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();t2.start();
        t1.join();//为了避免t1和t2都没执行完主线程就执行完了
        t2.join();
        System.out.println(i);

   }

}

给同一个实例加锁,这里需要注意的是两个线程都指向同一个实例instance,如果是两个不同的对象,比如

Thread t1 = new Thread(new AccountingSync());

Thread t2 = new Thread(new AccountingSync());

其实就是两把锁,就会导致无法预计的异常

public class AccountingSync implements Runnable{

       static AccountingSync instance = new AccountingSync();

       static int i = 0;

       public synchronized void increase(){

                i++;
    
       }

       public void run(){
        
            for(int j = 0;j<1000000;j++){

                increase();

            }
      }


    public static void main(String[] args) throws InterruptedException{

        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();t2.start();
        t1.join();//为了避免t1和t2都没执行完主线程就执行完了
        t2.join();
        System.out.println(i);

   }

}
 

给同一个类加锁:

如果将上面的increase()方法变成static的,即使两个线程指向不同的Runnable对象,但由于方法快请求的是当前类的锁,而非当前实例,因此,线程间还是可以正常同步。

public class AccountingSync implements Runnable{

       static AccountingSync instance = new AccountingSync();

       static int i = 0;

       public static synchronized void increase(){

                i++;
    
       }

       public void run(){
        
            for(int j = 0;j<1000000;j++){

                increase();

            }
      }


    public static void main(String[] args) throws InterruptedException{

        Thread t1 = new Thread(new AccountingSync());
        Thread t2 = new Thread(new AccountingSync());
        t1.start();t2.start();
        t1.join();//为了避免t1和t2都没执行完主线程就执行完了
        t2.join();
        System.out.println(i);

   }

}
 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值