互联网技术01——线程基础

线程安全的概念:

当多个线程访问某一个类(对象或方法)时,这个类始终能表现出正确的行为

synchronized :

可以加在任意对象及方法上,加上synchronized的这段代码称为“互斥区”或“临界去”

 实例  

1.  多个线程一个锁:

package com.company;

/**
 * Created by BaiTianShi on 2018/8/13.
 */
public class MyThread extends Thread {
    private int count = 5;
    public synchronized void run() {
        count--;
        System.out.println(this.currentThread().getName()+"count="+count);
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread,"t2");
        Thread t3 = new Thread(myThread,"t3");
        Thread t4 = new Thread(myThread,"t4");
        Thread t5 = new Thread(myThread,"t5");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

运行结果

t1count=4
t2count=3
t3count=2
t4count=1
t5count=0

可见,是按照顺序执行的。

当我们把run()方法的synchronized去掉时,运行结果如下:

t1count=3
t5count=0
t4count=1
t3count=2
t2count=3

去掉synchronized时,就出现了线程安全问题

引申出的锁竞争问题:

 当多个线程同时访问myThread的带有synchronized修饰的run方法时,以排队的方式进行处理,当一个线程拿到这个方法所得时候,其他线程想要执行这段代码必须不断尝试获得这把锁,直到拿到为止,当然,这不断的尝试的过程肯定需要占用cpu的开销的。当线程很多很多的时候,就会出现cpu资源占用过高,甚至直接宕机。

 

2.多个线程多个锁:

package com.company;

/**
 * Created by BaiTianShi on 2018/8/13.
 */
public class ManyThreadManyLock {
    private int num;

    public synchronized void privateNum(String arg){

            try {
                if(arg .equals("a")) {

                    num = 100;
                    System.out.println("arg a,为num赋值完毕");
                    Thread.sleep(1000);
                }else{
                    num= 200;
                    System.out.println("arg b,为num赋值完毕");
                }
                System.out.println("arg="+arg+";num="+num);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }

    public static void main(String[] args) {
        final ManyThreadManyLock my1 =new ManyThreadManyLock();
        final ManyThreadManyLock my2 =new ManyThreadManyLock();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                my1.privateNum("a");
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                my2.privateNum("b");
            }
        });
        t1.start();
        t2.start();
    }
}

1. t1和t2两个线程互不影响,都能获得 privateNum 方法,虽然privateNum是被synchronized修饰。这是因为线程持有的锁是对象所,我们创建的两个线程分别拥有两个对象,所以会互不影响。

2. 有一种情况是相同的在静态方法或静态常量上加synchronized。静态方法或常量是加synchronized表示这个类上锁,包括通过这个类创建的所有的对象,而不再指某个对象了。

 

下面我们聊聊对象锁的同步和异步

 

同步:synchronized

         同步的概念就是共享,我们要牢牢记住"共享"这两个字,如果不是共享的资源,就没有必要进行同步(举个简单例子,我们存款和取款,一定要同步,因为不同步的 话,资金就乱了,这是不能忍受的)。

异步:asynchronized

         异步的概念就是独立,相互之间不受到任何制约。就好像我们学习http的时候,在页面发起的Ajax请求,我们还可以继续浏览或操作页面的内容,二者之间没有任何关系。

         同步的目的就是为了线程安全,其实对于线程安全来说,需要满足两个特性:原子性(同步)和可见性。

         我们还是来一起看个例子。

示例三

         新建MyObject类,如下所示:

package com.internet.thread;
 
public class MyObject {
    public synchronized void method1(){
    	try {
			System.out.println(Thread.currentThread().getName());
			Thread.sleep(4000);
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
    
    public void method2(){
    	System.out.println(Thread.currentThread().getName());
    }
    
    public static void main(String[] args){
    	final MyObject mo = new MyObject();
    	/**
    	 * 分析:
    	 * t1线程先持有object对象的Lock锁,t2线程可以以异步的方式调用对象中的非synchronized修饰的方法
    	 * t1线程先持有object对象的Lock锁,t2线程如果在这个时候调用对象中的同步(synchronized)方法则需等待,也就是同步
    	 */
    	Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				mo.method1();
			}
		},"t1");
    	
    	Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				mo.method2();
			}
		},"t2");
    	
    	t1.start();
    	t2.start();
    }
}

 

  运行上面的main方法,我们会看到t1和t2同时打印出来了,这说明此时method1和method2是异步执行的。

         现在我们给method2方法也加上synchronized关键字

public synchronized void method2(){
    	System.out.println(Thread.currentThread().getName());
    }

 

再运行main方法,这回我们看到t1信息输出4秒后才会看到t2信息,出现这种情况的原因是,我们只new了一个对象,而synchronized关键字锁的便是对象,由于method1和method2现在都被synchronized关键字修饰,因此只要是访问该对象的这两个方法的线程都会进行同步操作,也就是说谁先拿到对象的锁谁便先执行,执行完之后,释放锁,这时下一个线程才能执行。

         总结下示例三:

         A线程先持有object对象的Lock锁,B线程如果在这个时候调用对象中的同步(synchronized)方法则需要等待,也就是同步。

        A线程先持有object对象的Lock锁,B线程可以以异步的方式调用对象中的非synchronized修饰的方法。

参考: https://blog.csdn.net/u012453843/article/details/72842502

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_28240551/article/details/81613170