线程与同步锁

进程和线程的区别

进程:计算机中特定功能的程序在数据集上的一次运行

线程:线程是进程的一个单元  线程是小的,进程是大的

多线程:一个进程中有多个线程在同时运行,如迅雷下载,迅雷软件的运行就是一个进程,在迅雷中可同时下载多个电影,这就是多线程(每一个下载都是一个线程)

JVM是多线程的,在运行JVM时候,后台会运行垃圾回收的线程,来清理没有被引用的对象。

线程的执行原理:

线程的并发执行是通过多个线程不断地切换CPU的资源,速度非常快,实际上同一时间只有一个线程在工作

因为速度非常快,所以所感知到认为是多个线程在并发执行(所以可以认为是同时执行)尽管多个线程不断切换CPU的资源,但速度太快,可认为是同时执行。

为什么要使用多线程?

子线程执行发送请求后,就会自动结束,那么就接收不了服务器响应的数据 子线程是无法通过return语句来返回数据的
所有的代码默认在主线程中运行
为一个任务开启子线程 说明这个任务是比较耗时间的 目的是保证执行该任务的同时其他任务也可以执行
否则,如果该任务执行在主线程,并且比较耗时间,则会出现主线程阻塞。


线程的生命周期

流程:

1.新建:线程被new出来

2.准备就绪:线程具有执行的资格,即线程调用了start()方法,还没有执行权利,因为要抢资源,所以调用了start不一定马上就执行

3.运行:抢到了CPU资源,则可运行

4.阻塞:没有继续执行的资格,sleep()时间到了进入准备就绪状态或notify()唤醒进入准备就绪状态或wait()等待

5.销毁:线程的对象编程垃圾,释放资源当Run()结束,则销毁。

线程的实现:

Thread

构造器:

Thread(Runnable target) 用于第二种方法创建线程对象

          分配新的 Thread 对象。

Thread(Runnable target, String name)  可指定线程的名字

          分配新的 Thread 对象。 

创建新执行线程有两种方法。

一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。

     class PrimeThread extends Thread {        用该类new出来的是对象
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }
         public void run() {
             // compute primes larger than minPrime
         }
     }

注:该类继承了父类Thread,可用该类来创建线程对象

另一种方法是声明实现 Runnable 接口的类。

     class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }
         public void run() {
             // compute primes larger than minPrime
         }
     }

采用第二种方法的优势:因为java是单继承的,所以如果该类继承了Thread就不能继承其他类,但用第二种方法,实现了Runnable,还可以继承其他类。形成了多线程的共享一个对象

启动线程:

 void start()

          使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

并发

并发问题:  如卖火车票,电商网站

解决方案:

针对线程的安全性问题,需要使用同步(就是要加锁,共享资源同一时间之只能一个线程访问)锁,

语法:

synchronized(锁对象){

  //操作共享资源的代码

}

同步代码加在

1.代码被多个线程访问

2.代码中有共享的数据

3.共享数据被多条语句操作

	private String name ;
	private static int tickets = 100;
	public Sale(String name) {
		super(name);
	}
	private static Object obj = new Object();
	//方法体只能在方法中,不能在外面,if-else语句块一定要加大括号
	public void run() {
	 while(true) {
		 	synchronized (obj) {
		 if(tickets > 0) {
			 System.out.println(this.getName()+"正在卖第"+tickets--+"张票");
		 }
		 else {
			 System.out.println("票已卖完");
		 	break;
		 }
		 }
	}
}

synchronized同步代码块的锁对象可以是任意类对象(线程的实现方式是继承于Thread这个对象必须是线程类共享的(静态的)


synchronized修饰在方法上

如果是静态方法,synchronized的锁对象就是类的类对象—类锁(synchronized加在线程体内)

synchronized如果加在非静态的对象方法上,那么它的锁是this—对象锁(synchronized加在线程体内)

因为static修饰的方法内不能用this,所以只能用类的类对象来表示锁

如果方法没有static修饰,那么在线程体内就可调用this来作为对象

类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。

同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

两个对象锁,只能运行一个,因为都是同一个对象。但是一个对象锁,一个类锁,都可以执行。

synchronized修饰在一般方法上

实现Runnable的子类创建的对象,相当于一把钥匙,Thread创建的线程使用这把锁进入同步代码块,如果两个线程共用这个对象,那么同一时间只能由一个线程进入这个同步代码块。如果创建了两个锁对象,两个线程分别运用这钥匙,但最后只能一个线程得到进入两个锁,那么两个线程都可以同时进入同步代码块里面。如果没有明确的锁对象,那么可以创建任意一个锁对象,任何线程都有机会拿这把锁。

synchronized修饰run()

public synchronized void method()
{
   // todo
}

public void method()
{
   synchronized(this) {
      // todo
   }
}

以上代码是等价的.

1.synchronized关键字不能继承。

2.在定义接口方法时不能使用synchronized关键字。

3.构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

synchronized修饰静态方法:

synchronized修饰的静态方法锁定的是这个类的所有对象。

语法:用synchronized修饰在普通的静态方法上,把静态方法放在synchronized修饰的run的方法体中。synchronized修饰的静态方法锁定的是这个类的所有对象,意思就是说,这个方法是类方法,所有这个类创建出来的对象可作为一个整体,实质上都是同一把锁。

 public synchronized static void method() {
		//todo
   }
   public synchronized void run() {
      method();
   }

注:以上代码如果创建了两个锁对象,那么分别给两个线程运行时,只能一个线程执行,因为两个锁对象是指上为同一把锁。

synchronized修饰在static方法内的语句块中:    修饰在类上 锁是类

   public static void method() {
      synchronized(SyncThread.class) {

      }
   }
   public synchronized void run() {
      method();
   }

注:因为修饰在同步代码上,且在静态方法中,那么这个锁对象就是类的所有对象,这个类的所有对象创建实质上都是共用同一把锁。


synchronized修饰代码块的优点:

synchronized来修饰代码块:synchronizeduser{user.test();}

那么这个方法加锁的对象是user这个对象,跟执行这行代码的对象没有关系,当一个线程执行这个方法时,这对其他同步方法是没有影响的,因为他们持有的锁完全不一样。意思是修饰代码块,锁对象和执行代码的对象不一样,所以其他对象可以访问其他的同步方法

synchronized的缺陷:

当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,

必须等待或者阻塞,这对高并发的系统是致命的,这很容易导致系统的崩溃。如果某个线程在同步方法里面发生了死循环,

那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。这是一个致命的问题。


总结:

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,

则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,

则它取得的锁是对类,该类所有的对象同一把锁。

B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值