多线程


多线程

线程是操作系统的一个重要概念,多线程是指程序中同时存在着好几个执行体,它们按几条不同的执行路线共同工作,独立完成各自的功能而互不干扰。例如:我们在听音乐的时候,还能同时上QQ聊天,还一边刷新浪微博,这都是多线程并发机制的功劳。

一.什么是进程,线程,它们的区别是什么?

进程是一个正在执行的程序,例如QQ,千千静听,360卫士等。

而线程是进程的某个单一顺序的控制流。一般是一个进程中包括多个线程。线程最大的特点是并发执行。由于线程的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程切换会非常快,以致于它们互相切换使我们感觉不到差别,令我们觉得始终是一个程序为我们服务。

如图:多线程

二.关于Java中多线程的基础知识

1.Java虚拟机启动时就有一个进程java.exe

2.最开始的时候jvm会开始调用main 线程先,然后在调用一下代码中的线程

3.写线程有两种方法:

  • 线程可以直接继承java.lang.Thread,然后覆盖run方法来实现自己的功能,再调用线程的start方法来启动线程。
  • 可以实现Runnable接口,必须实现run接口。在调用时再将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。如new Thread(new RunnableClass()).start();
  • 推荐使用第二种方法,因为java本身不支持多继承,利用接口可用性较强

4.线程如果没哟自定义名字通常使用默认名字 Thread-编号(编号从0开始)。

如图两种实现方法

继承Thread (其实我们看源代码可以知道,Thread类也实现了Runnable接口,因此也实现了run方法)

class Demo extends Thread
{
 public void run()
 {
  
   System.out.println("创建线程");
 }
}
Demo d = new Demo();//创建好一个线程。
 d.start();//开启线程并执行该线程的run方法。
实现Runnable接口

class Demo implements Runnable
{
 public void run()
 {
  
   System.out.println("创建线程");
 }
}
Demo d = new Demo();
 new Thread(d).start();//创建好一个线程,开启线程并执行该线程的run方法。
三 线程的生命周期,如图



1.创建新线程,调用start( )方法进入运行状态,此时应该有两个状态,当线程分配到CPU 资源时,它才有资格去运行,否则,线程进入堆栈,处于阻塞状态,等到分配到CPU资源在运行。

2.冻结状态,是运行状态通过sleep(time)或wait()方法使它暂时处于冻结状态,当睡眠时间够了,或者由程序员定义什么时候唤醒notify()方法改变状态。

3.销毁状态,此时释放CPU资源,线程灭忙。这有两个原因:一是正常运行的线程完成了它的全部工作。二是线程被强制终止。

注意:并不是要进入销毁状态才会释放CPU资源,CPU资源分配是不确定,这看哪个线程先抢到资源

四. 同步问题

代码是一条一条地执行,当执行到某句时候,线程A还没有执行到下一句时被抢了线程B的资源,线程B就执行下去包括线程A的没有执行的代码,此时线程B释放资源,线程A重新获得资源,继续执行,但因为两条线程共享了同一个数据,会出现问题。

class test_thread implements Runnable{

	private int num=50;
	public void run() {
		// TODO 自动生成的方法存根
		while(num>0)
			{
				try {
					Thread.sleep(10); //A执行到这里,被B抢去了CPU资源,B继续运行,当B刚好等于1时,减减,num为0,此时A不再判断而输出0;
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName()+"num="+num--);
	}
		
	}
	
}
public class Demo_thread {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		test_thread t1=new test_thread();
		new Thread(t1).start();
		new Thread(t1).start();
		
	}

}
输出:......

Thread-0num=1
Thread-1num=0

解决方法:引入锁的概念

synchronized:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。

1.当方法用synchronized 时,就像加上了一把锁,线程A进去了,只有等到线程A执行完这个程序块,线程B才能进入去执行。解决了共享同一个数据的问题。就像我们正常谈恋爱一样,当一对情侣正在热恋的时候,我们就只能等他们分手时才能追求女孩,这个"恋爱锁"是只能等他们分手后才解锁,我们(线程B)才能追求,成功后我们也是加了一把锁,别人也干扰不进来。

synchronized(对象)
{
	需要被同步的代码

}

2.加同步代码块的地方首先要明确两点

  • 必须有两个或两个以上的线程
  • 必须多个线程使用的是同一把锁
3.一般synchronized(对象)里面写的写this,表示本类。
4.静态的同步方法,内存中没有本类对象,此时使用的锁是该方法所在类的字节码文件对象。 类名.class
五.产生死锁
什么是死锁?
两个或两个以上的线程 在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。如下,A需要B的锁才能执行下去,但B也要A的锁才执行下去,A没有解,就导致等待下去。
一个经典例子:三个鸡蛋和三份面粉做成一块蛋糕。现在总共有三个鸡蛋和三份面粉,甲现在有其中的三个鸡蛋,乙现在有其中的三份面粉,两个都需要对方的东西,但都不肯放手,这就产生了死锁。
public class P03_11 implements Runnable {
	static Object S1 = new Object(), S2 = new Object();


	public void run() {
		if (Thread.currentThread().getName().equals("th1")) {
			synchronized (S1)


			{
				System.out.println("线程1锁定S1"); 
				try {
					Thread.sleep(1000);
				} catch (Exception ex) {
				}
				synchronized (S2) {
					System.out.println("线程1锁定S2"); 
				}
			}
		} else {
			synchronized (S2) {
				System.out.println("线程2锁定S2");


			}
			synchronized (S1) {
				System.out.println("线程2锁定S1"); 
			}
		}
	}


	public static void main(String[] args) {
		Thread t1 = new Thread(new P03_11(), "th1");
		Thread t2 = new Thread(new P03_11(), "th2");
		t1.start();
		t2.start();
	}
}


因此,在我们编代码时,应该尽量避免死锁。

六:wait() 和notify()
1. 前提是必须是同一锁上。
2.只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
3.Thread类的 sleep方法,在睡眠的过程中是不会释放掉对象的锁的
4.notifyAll() 是唤醒所有线程



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值