多线程二:线程的基本操作,线程间的同步,synchronized,Happen-Before等

什么是线程

线程是进程内的执行单元

例如 下面36417,就是进程

通过 命令,看到的,就是进程号37417里的线程

jstack 36417

线程的状态

线程的状态,本来想找个图片的,看到百度经验这篇,说得挺好,挺详细,就附上链接吧

https://jingyan.baidu.com/article/f79b7cb33886ef9144023ebf.html

线程的基本操作

创建线程的三种形式

1.直接继承一个Thread类,重写run方法

2.实现Runnable的接口,实现run方法,这种形式,是我平时工作中,使用最多的形式 3.创建一个Future接口的实现类 FutureTask,这种形式有返回值,并且可以抛出异常

需要注意的是,调用start方法,才是执行线程,如果只是执行run方法,那么程序会当成普通方法运行;

代码如下

public class TestThread {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName());
                return 10;
            }
        });
        Thread thread = new Thread(futureTask);
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        Thread thread2 = new Thread(runnable);
        thread.start();
        thread1.start();
        thread2.start();
        try {
            thread.join();
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(futureTask.get());
    }
}

线程的一些常见方法:

Thread.stop() 线程终止;不推荐使用,它会释放所有monitor监控,本来运行中的线程,或者运行一半,任务还没有执行完成,也会被终止

public void Thread.interrupt() // 中断线程 public boolean Thread.isInterrupted() // 判断是否被中断 public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态

suspend() ; 挂起;suspend()不会释放锁,如果加锁发生在resume()之前 ,则死锁发生 resume();继续执行

join(); join(long millis);等待线程结束

yeild();谦让;是指空出cpu当前的运行周期,等待下一轮cpu的时间片

守护线程

在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程

当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出

Thread thredTest = new Thread(() ->{
            System.out.println(Thread.currentThread().getName());
        }) ;
				//标记为守护线程
        thredTest.setDaemon(true);
        thredTest.start();

线程优先级

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

Thread high= new Thread(() ->{
            System.out.println(Thread.currentThread().getName());
        }) ; 
Thread low= new Thread(() ->{
            System.out.println(Thread.currentThread().getName());
        }) ; 
high.setPriority(Thread.MAX_PRIORITY); 
low.setPriority(Thread.MIN_PRIORITY); 
low.start();
high.start();

高优先级的线程更容易再竞争中获胜,更容易获得cpu的临幸

基本的线程同步操作

synchronized

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

//实例对象:同步代码块,锁住的该类的实例对象
synchronized(this){
  
}
//class对象:锁住的该类的类对象
synchronized(Test.class){
  
}

//任意实例对象:锁住的是该创建的实例对象,下例是对lock对象加锁
Object lock = new Obejct();
synchronized(lock){
  
}

对方法加锁

//作用在方法上---实例方法:类的对象实例,锁住的是该类的实例对象;
//相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
public synchronized void test(){
	//........
}
//作用在方法上---静态方法:锁住的类的对象;相当于对当前类加锁,进入同步代码前要获得当前类的锁。
public static synchronized void test1(){
  //.....
}

Object.wait() Obejct.notify()

首先wait方法和notify方法,是在synchronized关键词中使用的,也就说,在执行方法之前,肯定是获取到了对应的锁操作;

举例说明:A线程执行的时候,会释放当前获取到的锁,让出cpu的执行的时间,让线程进入等待状态,直到B线程获取锁的对象,执行对象的notify方法,才会继续执行;需要注意的时候,notify方法,不会释放锁,只有B线程执行完了,A线程才会继续获取对象锁,继续执行

public static class T1 extends Thread{ 
  public void run(){
    synchronized (object) {
      System.out.println(System.currentTimeMillis()+":T1 start! "); 
      try {
        System.out.println(System.currentTimeMillis() +":T1 wait for object ");
        object.wait();
    } catch (InterruptedException e) {
      e.printStackTrace(); 
    }
      System.out.println(System.currentTimeMillis()+":T1 end!"); 
  }
}
                                      
public static class T2 extends Thread{ 
  public void run(){
  	synchronized (object) {
  		System.out.println(System.currentTimeMillis() +":T2 start! notify one thread");
  		object.notify(); System.out.println(System.currentTimeMillis()+":T2 end!"); 
      try {
  			Thread.sleep(2000);
  		} catch (InterruptedException e) { 
      }
  } }
}                 

最终的执行结果的顺序,也是可以看出来的

1566523411000:T1 start!

1566523411000:T1 wait for object

1566523411000:T2 start! notify one thread

1566523411000:T2 end!

1566523411000:T1 end!

Happen-Before

程序顺序原则:一个线程内保证语义的串行性

volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性

锁规则:解锁(unlock)必然发生在随后的加锁(lock)前

传递性:A先于B,B先于C,那么A必然先于C

线程的start()方法先于它的每一个动作

线程的所有操作先于线程的终结(Thread.join())

线程的中断(interrupt())先于被中断线程的代码

对象的构造函数执行结束先于finalize()方法

原创文章 83 获赞 155 访问量 36万+

猜你喜欢

转载自blog.csdn.net/qq_28410283/article/details/100030596