多线程总结一:基本概念

一.普通对象,线程对象,线程

  1. 普通对象:针对于线程对象而言的。到处可见普通对象,Person p = new Person();一个无头无脑的人就是一个普通对象。
  2. 线程对象:线程类的实例对象。我自己定义了一个线程类public class CheckThread extends Thread{...},然后CheckThread ct = new CheckThread(),ct就是线程对象。
  3. 线程:代码的一次执行过程。
  4. 从概念层面讲,普通对象和线程对象是实实在在的东西,线程是虚的东西。线程好比一条细线。把普通对象和线程对象上的方法串起来执行,得到想要的结果而已。
  5. 从内存层面讲,对象就是一块内存地址,里面有数据和运行方法。而线程就是这些方法的一次执行路径。
  6. 唯一觉得线程对象和线程有点关系的地方:线程对象定义了线程的一个入口(线程对象的run方法),就好像main方法开启了主线程的一个入口一样。

二.锁和同步

  1. 锁是针对对象或者类而言的。Java中每个对象都有一个内置锁。
  2. 同步的表现形式为synchronized方法或者synchronized代码块,作用为同一时刻,只能有一个线程调用此方法或代码块。当程序运行到synchronized同步方法或代码块时锁才起作用。
  3. 对象锁:
    1. 线程1调用synchronized getX()方法时,有幸获得对象锁,会把对象锁带在自己身上,此时线程2想进入synchronized getX()方法或者synchronized getY()方法等任何非静态同步方法时,看到synchronized就需要对象锁,由于对象锁被线程1占有者,所以线程2只能等待。
    2. 线 程1调用getX()的synchronized代码块时,有幸获得对象锁,会把对象锁带在自己身上,此时线程2可以进入getX()或者getY()方 法,但进入后什么时候发现synchronized代码块,就什么时候需要对象锁,由于对象锁被线程1占有者,所以线程2只能等待。
    3. 如果一个类有有两个实例A和B,线程1调用对象A的synchronized getX()方法时,用到对象A的锁,线程2调用对象B的synchronized getX()方法时,用到对象B的锁,两个锁不一样,所以两个线程可以同时进行。
      public synchronized int getX() {
              return x++;
      }
      public synchronized int getY() {
              return y++;
      }
      public int getX() {
              synchronized (this) {
                  return x;
           }
       }
      public int getY() {
              synchronized (this) {
                  return y;
           }
       }
  4. 类锁:和对象锁差不多的
    1. 线程3调用synchronized static setName()方法时,有幸获得类锁,会把类锁带在自己身上,此时线程4想进入synchronized static setName()方法或者synchronized static setAge()方法等任何静态同步方法时,看到synchronized就需要类锁,由于类锁被线程3占有者,所以线程4只能等待。
    2. 线 程3调用static setName()的synchronized代码块时,有幸获得类锁,会把类锁带在自己身上,此时线程4可以进入setName()或者setAge()方法等任何静态同步方法时,但进入后什么时候发现synchronized代码块,就什么时候需要类锁,由于类锁被线程3占有者,所以线程4只能等待。
    3. 区别于对象的是,类的静态方法是有一份,多个线程都想调用此同步静态方法时,同一时刻肯定只能有一个线程能调用。
      public static synchronized int setName(String name){
            Xxx.name = name;
      }
      public static synchronized int setAge(String age){
            Xxx.age= age;
      }
      
      public static int setName(String name){
            synchronized(Xxx.class){
                  Xxx.name = name;
            }
      }
      public static int setAge(String age){
            synchronized(Xxx.class){
                  Xxx.age= age;
            }
      }
       
  5. 线程1对静态同步方法的调用用到类锁,线程3对非静态同步方法的调用用到对象锁,它们之间不会产生阻塞。
  6. 一个对象可以同步方法和非同步方法同时存在,线程对同步方法的调用需要获得锁,对非同步方法的调用不需要。
  7. 总结线程一看到synchronized方法或代码块时三步曲,每个线程来的时候你都这么想一下,就理清头绪了。
    1. 线程要搞清楚要得到什么样的锁才能进入这个方法或代码块。
    2. 这把锁现在是什么状态。
    3. 线程拿到了这把锁后一起把锁带入方法或代码块里,直到方法和代码块区域执行完后释放锁。

三.测试用例

     引用下greatwqs 的例子,1234线程组合式的运行其中两个,能证明上面说的每一点。

package com.zzy.Thread;
public class ThreadTest {
	public static void main(String[] args) {
		final MyObject obj = new MyObject();
		final MyObject obj2 = new MyObject();
		//线程1
		Runnable r1 = new Runnable(){
			public void run(){
				obj.getName(1);
			}
		};
		Thread t1 = new Thread(r1);
		t1.start();
		//线程2
		Runnable r2 = new Runnable(){
			public void run(){
				obj.getAge(2);
			}
		};
		Thread t2 = new Thread(r2);
		t2.start();
		//线程3
		Runnable r3 = new Runnable(){
			public void run(){
				MyObject.getNameStatic(3);
			}
		};
		Thread t3 = new Thread(r3);
		t3.start();
		
		//线程4
		Runnable r4 = new Runnable(){
			public void run(){
				obj2.getName(4);
			}
		};
		Thread t4 = new Thread(r4);
		t4.start();
	}
}
class MyObject {
	public String getName(int i) {
		synchronized (this) {
			try {
				System.out.println("线程" + i + ":getName sleep 10 secs start");
				Thread.sleep(1000 * 10);
				System.out.println("线程" + i + ":getName sleep 10 secs end");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return "";
		}
	}
	public String getAge(int i) {
		synchronized (this) {
			try {
				System.out.println("线程" + i + ":getAge sleep 10 secs start");
				Thread.sleep(1000 * 10);
				System.out.println("线程" + i + ":getAge sleep 10 secs end");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return "";
		}
	}
	
	public static String getNameStatic(int i) {
		synchronized (MyObject.class) {
			try {
				System.out.println("线程" + i + ":getNameStatic sleep 10 secs start");
				Thread.sleep(1000 * 10);
				System.out.println("线程" + i + ":getNameStatic sleep 10 secs end");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return "";
		}
	}
}
 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值