线程

  • 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
  • 实现一个线程:
extends Thread(重写run方法)

implements Runnable
  • synchronized:可以在任意对象方法上加锁,而加锁的这段代码称为互斥区,或临界区。
  • 注:一个线程想要执行syschronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁执行syschronzied代码体内容,拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时竞争这把锁。(也就是会有锁竞争的问题)。
 1 public class MyThread extends Thread{
 2 
 3     private int count=5;
 4 
 5     public synchronized void run() {
 6         count--;
 7         System.out.println(this.currentThread().getName()+":"+count);
 8     }
 9     
10     public static void main(String args[]) {
11         
12         MyThread thread=new MyThread();//当前线程
13         Thread t1=new Thread(thread,"1号线程");
14         Thread t2=new Thread(thread,"2号线程");
15         Thread t3=new Thread(thread,"3号线程");
16         Thread t4=new Thread(thread,"4号线程");
17         Thread t5=new Thread(thread,"5号线程");
18         t1.start();
19         t2.start();
20         t3.start();
21         t4.start();
22         t5.start();
23         //按照线程启动,结果应该为:43210,但是run方法不加synchronized达不到预期结果
       //使用
synchronized可以锁住但是存在锁竞争问题
24     }
25 }
  • 多个线程多个锁
  • 注:关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当作多个对象通过多个线程访问synchronized修饰的方法时,线程拿到的是自己指定对象的锁(不是相同的锁),这种情况下会导致结果达不到预期,要想多线程不同对象拿到相同的锁,可以将synchronzied修饰的方法改为静态的(static),表示锁定.class类,类一级别的锁(独占.class类)
public class MultiThread {

    private static int num=0;
    
    //static
    public static synchronized void printNum(String tag) {
        try {
            if(tag.equals("a")) {
                num=100;
                System.out.println("a");
                Thread.sleep(2000);
            }else {
                num=200;
                System.out.println("b");
            }
            System.out.println(tag+":"+num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public static void main(String args[]) {
        MultiThread multiThread1=new MultiThread();        
        MultiThread multiThread2=new MultiThread();
        //线程1
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                multiThread1.printNum("a");
            }
        });
        //线程2
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                multiThread2.printNum("b");
            }
        });
        t1.start();
        t2.start();    
        //预期结果应该是:a,a:100,b,b:200;但是即使方法加上synchronzied修饰,结果也达不到预期
        //2个线程分别得到的是multiThread1与multiThread2的对象锁,达不到同步的效果
        //要想达到效果可以将方法改成静态的,此时属于类(MultiThread)一级别的锁
        //2个线程要做到不同时调用printNum方法,锁.class类  
    }
}
  • 同步与异步
  1. 同步:synchronzied同步的概念就是共享,不是共享资源没必要同步同步的目的就是为了线程安全,线程安全条件:原子性(同步)可见性。
  2. 异步:asynchronized异步的概念就是独立,互相之间不受任何制约(类似Ajax)。
  • 脏读
1.当数据库的一个事务A正在使用一个数据但还没有提交,另外一个事务B也访问到了这个数据,还使用了这个数据,这就会导致事务B使用了事务A没有提交之前的数据。(如果当前事务A回滚,那么事务B拿到的就是脏数据)

2.线程A去查某一数据,在还没有拿到该数据的时候,线程B修改了该数据,线程A拿到的是B没修改之前的数据,这是由于数据库一致性读的特性造成的。(在数据库删改的过程中,会将数据存入快照中,如果监听到数据有改动会去快照中找旧数据,所以拿到的是之前的数据)
  • synchronized锁重入:使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时可以再次得到该对象的锁。
/**
 * synchronized锁重入
 */
public class SyncDubbo1 {

    public synchronized void method1() {
        System.out.println("1");
        method2();
    }
    public synchronized void method2() {
        System.out.println("2");
        method3();
    }
    public synchronized void method3() {
        System.out.println("3");
    }
    
    public static void main(String args[]) {
        SyncDubbo1 dubbo=new SyncDubbo1();
        //线程1
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                dubbo.method1();
            }
        });        
        t1.start();
    }
}
  • 父子继承关系中要想实现同步,都得使用synchronized,否则存在线程安全问题。
/**
 * 存在继承关系时,synchronzied修饰的父类子类方法,可以实现线程安全
 */
public class SyncDubbo2 {
    
    static class A{
        public int i=10;
        
        public synchronized void add(){
            try {
                i--;
                System.out.println("A:"+i);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    static class B extends A{
        
        public synchronized void add(){
            try {
                while(i>0){
                    i--;
                    System.out.println("B:"+i);
                    Thread.sleep(1000);
                    super.add();
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    public static void main(String args[]){
        //线程1
        Thread t1=new Thread(new Runnable() {                    
            @Override
            public void run() {
                // TODO Auto-generated method stub
                B b=new B();
                b.add();
            }
        });        
        t1.start();
    }
}
  • synchronized使用String的常量加锁,会出现死循环问题。
  • volatile关键字volatile关键字主要作用是使变量多个线程之间可见。
  1. volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现多个线程间的变量可见,也就是满足线程安全的可见性。
  2. volatile不具备同步性(原子性),可以算是一个轻量级的synchronized,性能要比synchronzied强大很多,不会造成阻塞(netty底层大量使用volatile)。
  3. volatile用于多个线程之间变量可见,不能代替synchronzied的同步功能。(AtomicInteger类可以实现原子性,但是它只保证本身方法的原子性,不能保证多个方法的原子性)。
  • 线程通讯
  1. while轮询的方式(通过条件判断是否达到实现通讯,用volatile)。
  2. 同步synchronized:线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通讯就成为整体的必要方法之一
  3. 使用wait/notify:waitnotify必须配合synchronized关键字使用。wait方法释放锁,notify方法不释放锁

 

原文地址:https://www.cnblogs.com/LJing21/p/10632037.html