java多线程(加深理解)

java多线程(加深理解)

1.进程和线程的概述

1.1进程和线程关系
一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
一个程序至少有一个进程,一个进程至少有一个线程,同时线程不能脱离进程单独存在。

1.2进程和线程的区别
进程有独立的地址空间,一个进程崩溃后,不会对其他的进程产生影响;而线程只是一个进程中的不同执行路径,线程有自己的堆栈和局部变量,线程没有单独的地址空间。

思考:为什么运行我们运行的java程序的时候要先启动JVM虚拟机?
因为:启动JVM虚拟机相当于启动一个进程

java中的多线程

为什么要编写多线程程序,单线程程序不好吗?
因为:为了提高程序的执行效率,很多应用中都会采用多线程,这样就可以将任务分解以及并行执行,从而提高程序的运行效用。

Thread类和Runnable接口

1.Thread类中的run方法
线程对象中的run()方法,就是线程独立运行后,必须要执行的方法,如果我们有什么代码交给一个线程独立运行,那么就需要把这些代码放到run()方法中。

2.Thread类中的start方法
我们并不能直接调用一个线程对象的run方法,必须通过线程对象调用start方法,然后这个线程会自动的调用run方法的,如果直接调用run方法,那就不是多线程了。

3.创建和启动线程
第一种方式:创建Thread的子类对象,子类中重写run方法

	//如果需要,可以考虑使用匿名内部类
	Thread t = new Thread(){
		public void run(){
			//代码...
		}
	};
	//启动线程
	t.start();

第二种方式:创建Thread类对象,在构造器中出入Runnable接口的实现类,实现类中重写run方法

	//如果需要,可以考虑使用匿名内部类
	Thread t = new Thread(new Runnable(){
	public void run(){
		//代码...
		}
	});
	//启动线程
	t.start();

直接调用线程对象对象run方法和start方法后有什么不同
Thread t = new Thread
t.run() ===》直接调用run方法,就是没有启动线程,只是调用了t对象的run方法而已,这时会把t里的run方法是main这个线程里的run方法
t.start() —>会自动启动一个线程,这个线程会自动调用run方法,启动线程后就和main线程没关系了

简单例子:

public class ThreadDemo2 {
	//名字叫做“main” 的编程,任务:就是执行,main方法
	public static void main(String[] args) {
		
		//Thread  表示一个线程
		//任务:执行run方法中的代码
		Thread t = new Thread() {
			@Override
			public void run() {
				
				//这里就是另一个一个线程,需要执行的代码
				//Thread-0:Hello World   Thread-0:线程默认的名字,再new一个则是Thread-1....
				System.out.println(Thread.currentThread().getName()+":Hello World");
				
			}
		};
		//启动线程
		t.start();

		System.out.println(Thread.currentThread().getName()+":123");
		
	}

}

运行截图:
在这里插入图片描述

4.线程对象的状态
通常对线程对象状态的描述

初始化状态
就绪状态
运行状态
死亡状态
阻塞状态(阻塞在等待池、阻塞在锁池、其他情况阻塞)
在这里插入图片描述

5.Thread类中的常用方法
第一类:静态方法
public static int activeCount()
返回当前线程的线程组中活动线程的数目

public static Thread currentThread()
		返回当前正在执行的线程对象的引用

	public static int enumerate(Thread[] tarray)
		将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中

	public static void sleep(long millis)
		让当前线程在指定的毫秒数内休眠

	public static void yield()
		暂停当前运行的线程,让给其他线程使用CPU执行

	public static boolean holdsLock(Object obj)
		判断当前线程先是否拿着指定的锁

第二类:非静态方法

	public void start()
		线程启动时必须调用的方法

	public long getId()
		返回该线程的标识符,线程ID是一个正的long数,在创建该线程时生成。线程ID是唯一的,线程终止时,该线程ID可以被重新使用 

	public void setName(String name)
		设置该线程的名称
		
	public String getName()
		返回该线程的名称
	
	public int setPriority()
		设置线程的优先级
	public int getPriority()
		返回线程的优先级

	public Thread.State getState()
		返回该线程的状态

	public ThreadGroup getThreadGroup()
		返回该线程所属的线程组

	public boolean isAlive()
		测试线程是否处于活动状态
	
	public void setDaemon(boolean on)
		将该线程标记为守护线程或用户线程,默认false表示用户线程
	public boolean isDaemon()
		测试该线程是否为守护线程
	
	public void join()
		当前线程等待某个线程执行结束
		
	public void join(long millis)
		给定一个等待的限定时间
		
	public void run()
		线程要执行的代码在此方法中
		注:我们并不能直接调用run方法,而且启动线程后让线程自动调用run方法

第三类:中断的方法

	public void interrupt()
		中断线程
		
	public boolean isInterrupted()
		测试线程是否被中断   ====>检查是否有别的线程调用自己的interrupt方法,想打断自己的阻塞状态
		
	public static boolean interrupted()
		测试当前线程是否被中断
		
isInterrupted和interrupted的返回值就是这个boolean类型的值
	区别在于静态方法interrupted在返回boolean值后,会把这个打断的标示符给清理掉,而且非静态方法isInterrupted不会清理

6.数据共享:俩个或多个线程可以共享对象中的数据

静态变量:局部变量【一定是】多个线程共享的数据
实例变量:局部变量【可能是,也可能不是】多个线程共享的数据,这个是或者不是取决于编写代码的结构
局部变量:局部变量【一定不是】多个数据共享数据

7.线程同步

synchronized关键字是加锁的意思,用它来修饰方法就表示给该方法加了锁,从而达到线程同步的效果;
用它来修饰代码块就表示给该代码块加了锁,从而达到线程同步的效果。
> 线程同步是目的
加锁是手段

	对谁加锁?
		代码块
		非线程安全的代码块  加锁  变成线程安全的代码块
	用这把锁的是谁?	(使用哪一个对象来充当这把锁)
		java中每一个对象,都可以是一把锁

 注意:可以用同一个对象锁不同的方法

在java中,使用synchronized关键字来实现线程同步的效果。关键字可以用来修饰方法,也可以直接作用到某段代码上

例1:synchronized关键字加在方法上

public class Test{
	private int x;
	public synchronized void test(){
		String name = Thread.currentThread().getName();
		for(int i=0;i<100;i++){
			x++;
		}
		System.out.println(name+": x="+x);
	}
}

例1:synchronized关键字加在代码块上

	public class Test{
		private int x;
		public void test(){
			String name = Thread.currentThread().getName();
			synchronized(this){
				for(int i=0;i<100;i++){
						x++;
				}
			}
			System.out.println(name+": x="+x);
		}
	}

8.线程通信(wait方法,notify方法,notifyAll方法)
这几个方法只能是在synchronize关键字在使用时,才能被用来充当锁的对象来调用

(1).wait():默认是方法中的this来充当这把锁,this.wait()。
(2).notify():当一个被锁的线程调用了notify方法是,那么可以随机唤醒在等待池中等待这把锁的一个线程。
(3).notfiyAll():与notify方法类似,不同之处在于,notifyAll方法会叫醒等待池中等待这把锁的所有线程。

9.死锁
在程序中是不允许出现死锁情况,一旦发生那么只能是手动停止JVM的运行,然后查找并修改产生死锁的问题代码。
简单的的描述死锁就是:两个线程t1和t2,t1拿着t2需要等待的锁不释放,t2又拿着t1需要等待的锁不释放。

下面是一个死锁的例子:

public class ThreadDeadLock extends Thread{
	private Object obj1;
	private Object obj2;
			
	public ThreadDeadLock(Object obj1,Object obj2) {
		this.obj1 = obj1;
		this.obj2 = obj2;
	}

	public void run() {
		String name = Thread.currentThread().getName();
		if("Thread-0".equals(name)){
			while(true){
				synchronized (obj1) {
					synchronized (obj2) {
						System.out.println(name+" 运行了..");
					}
				}
			}
		}
		else{
			while(true){
				synchronized (obj2) {
					synchronized (obj1) {
						System.out.println(name+" 运行了..");
					}
				}
			}
		}
	}
			
	public static void main(String[] args) {
		Object obj1 = new Object();
		Object obj2 = new Object();
		Thread t1 = new ThreadDeadLock(obj1,obj2);
		Thread t2 = new ThreadDeadLock(obj1,obj2);
		t1.start();
		t2.start();
	}
}

线程图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YJY@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值