多线程

本文详细介绍了多线程的基本概念,包括进程与线程的区别、多线程的实现方式(继承Thread类和实现Runnable接口)、线程状态转换、线程同步以及死锁的解决办法。同时,探讨了线程通信的重要性及其相关方法,如wait、notify和notifyAll。
摘要由CSDN通过智能技术生成

多线程的基本概念_程序_进程_线程

一、

程序:指令的集合

进程:正在执行的程序是一个静态的概念,进程是程序的一次静态执行的过程,占用特定的地址空间。

每个进程都是独立的,由cpu,data,code三部分组成。

缺点:内存的浪费,cpu的负担。

线程:进程中的一个单一的连续控制流程,轻量级进程。

一个进程可以有多个并行的线程

一个进程中的线程共享相同的内存单元,地址,空间。可以访问相同的变量和对象。

从同一个堆中分配对象

由于线程间的通信是在同一地址进行,所以不需要额外的通信机制,使得通信更简便,更快速。

二、

进程与线程的区别

一个进程中包含了多个线程,线程结束进程不一定结束。

进程结束,线程都结束。

cpu的调度执行的就是线程。

多线程的实现两种方式

通过继承Thread类实现多线程

1、继承Thread类

2、重写run()方法

3、通过start方法启动线程

package cn.xjion.pro11;
/**
 * mian线程和mt线程会并行,一起执行
 * @author xjion
 */
public class MyThread extends Thread{
//	重写run方法
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 10; i++) {
			System.out.println("Thread---"+i);
		}
	}
	public static void main(String[] args) {
//		创建线程类的对象
		MyThread mt = new MyThread();
//		启动线程
		mt.start();
//		main线程的for循环
		for (int i = 0; i < 10; i++) {
			System.out.println("main---"+i);
		}
	}
}

一定的缺点:Java中的类是单继承,一旦继承了Thread类就没办法继承其他类了。

实现Runnable接口实现多线程

1、编写类实现Runnable接口

2、实现run()方法

3、通过Thread类的start()方法启动线程

package cn.xjion.pro11;
/**
 * 通过继承Runnable接口实现线程的并行
 * @author xjion
 */
public class MyRunnable implements Runnable{
//	重写run方法
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 10; i++) {
			System.out.println("Run---"+i);
		}
	}
	public static void main(String[] args) {
//		创建实现Runnable接口的类的对象
		MyRunnable mr = new MyRunnable();
//		创建线程对象thread传入实现Runnable的类
		Thread thread = new Thread(mr);
//		创建Thread线程,传入创建线程类的对象
//		Thread thread = new Thread(new MyRunnable());
//		通过调用Thread中的start方法开启线程
		thread.start();
//		main线程的循环
		for (int i = 0; i < 10; i++) {
			System.out.println("main---"+i);
		}
	}
}

静态代理模式

Thread --> 代理角色

MyRunnable --> 真是角色

代理角色与真实角色实现共同的接口Runnable接口

package cn.xjion.pro11;

public interface Marry {
	void marry();
}
package cn.xjion.pro11;

public class You implements Marry{

	@Override
	public void marry() {
		// TODO Auto-generated method stub
		System.out.println("结婚咯,好开心啊");
	}
}
package cn.xjion.pro11;

public class MarryCompany implements Marry{
	//重写marry方法
	@Override
	public void marry() {
		// TODO Auto-generated method stub
//		婚前
		this.before();
//		m结婚
		m.marry();
//		婚后
		this.after();
	}
//	私有结婚对象
	private Marry m;
//	构造器
	public MarryCompany(Marry m) {
		super();
		this.m = m;
	}
	public void after(){
		System.out.println("收拾东西,非常开心!!!");
	}
	public void before(){
		System.out.println("婚前准备,非常激动!!!");
	}
}
package cn.xjion.pro11;

public class TestMarry {
	public static void main(String[] args) {
//		创建真实角色
		Marry m = new You();
//		创建代理角色
		Marry myc = new MarryCompany(m);
//		调用结婚
		myc.marry();
	}
}

线程的状态

新生状态

用new关键字建立一个线程对象后,改线程对象就处于新生状态

处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态

就绪状态

具备了运行条件。但还没有被分配到cpu,一旦获得cpu,线程就进入运行状态并自动调用run方法

导致线程进入就绪的四种方法:

      1. 新建线程:调用start()方法,进入就绪状态;

      2. 阻塞线程:阻塞解除,进入就绪状态;

      3. 运行线程:调用yield()方法,直接进入就绪状态;

      4. 运行线程:JVM将CPU资源从本线程切换到其他线程。

运行状态

线程执行自己的run方法,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡

阻塞状态

阻塞是暂停线程执行以等待某个条件发生

导致阻塞的四种原因

      1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。

      2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。

      3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。

      4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。

死亡状态

两种情况:

1、正常结束,完成了全部工作结束。

2、被强制终止,如调用stop()或destroy()方法来终止一个线程,但不推荐使用

当线程死亡了就不能再进入其他状态了。

package cn.xjion.pro11;
/**
 * 终止线程的典型方法
 * @author xjion
 */
public class TestThreadCiycle implements Runnable{
	String name;
//		用来判断线程什么时候结束
	boolean live = true;
//	构造器
	public TestThreadCiycle(String name) {
	super();
	this.name = name;
}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		int i = 0;
//当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
		while(live){
			System.out.println(name+(i++));
		}
	}
	public void terminate(){
		live = false;
	}
	public static void main(String[] args) {
		TestThreadCiycle ttc = new TestThreadCiycle("线程1:");
//		新生态
		Thread thread = new Thread(ttc);
//		就绪态
		thread.start();
//		循环打印main线程
		for (int i = 0; i < 10; i++) {
				System.out.println("main线程---"+i);
		}
//		调用方法把live赋值false
		ttc.terminate();
		System.out.println("ttc停止运行");
	}

}

获取线程基本信息的方法

常用方法

序号方法名称描述
1static Thread currentThread()返回目前正在执行的线程
2final String getName()返回线程的名称
3final boolean isAlive()判断线程是否处于活动状态
package cn.xjion.pro11;
public class TestThreadMethod implements Runnable{
	@Override
	public void run() {
		Thread t = Thread.currentThread();
		System.out.println(t);
	}
	public static void main(String[] args) {
		Thread t = Thread.currentThread();
		//toString()方法得到的内容为[线程名称,线程的优先级,线程组的名称]
		System.out.println(t);
//		创建对象
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(my);
		Thread t2 = new Thread(my);
		Thread t3 = new Thread(my);
//		线程是同时开启,并行的
		t1.start();
		t2.start();
		t3.start();
		
		/**在Thread类中一定有一个静态变量int,用于统计创建线程的个数*/
		//线程的默认命名规则  Thread - int类型的变量值
	}
}
package cn.xjion.pro11;
public class TestGetName {
	public static void main(String[] args) {
		//主线程的名称
		Thread t = Thread.currentThread();
		String name = t.getName();
		System.out.println("主线程的名称" + name);
		
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(my,"自定义线程1");
		Thread t2 = new Thread(my,"线程2");
		Thread t3 = new Thread(my,"线程3");
		System.out.println(t3.getName());
		
		t1.start();
		t2.start();
		t3.start();
	}
}
package cn.xjion.pro11;
public class TestAlive extends Thread{
	@Override
	public void run() {
		for (int i = 0;i < 10;i++) {
			System.out.println(Thread.currentThread().getName() + "--------------->" + i);
		}
	}
	public static void main(String[] args) {
		//主线程
		MyThread my = new MyThread();
		System.out.println("新生状态的线程是否处于活动状态:"+ my.isAlive());
		my.start();//启动线程
		System.out.println("线程my处于就绪状态的线程是否处于活动状态:" + my.isAlive());
		//主线程中的循环
		for (int i = 0;i < 10;i++) {
			System.out.println("----------"+Thread.currentThread().getName()+"--------->"+i);
		}
		//主线程中的最后一句代码
		System.out.println("my线程是否处于活动状态:" + my.isAlive());
	}
}

多线程的安全性问题

package cn.xjion.pro11;
public class Ticket implements Runnable{
	private int ticket = 5;
	
	@Override
	public void run() {
		for (int i=0;i<100;i++) {//每个窗口排了100个人
			if (ticket > 0) {//有票
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在卖第" + (ticket--)+"张票");
			}
		}	
	}
}
package cn.xjion.pro11;
public class TestTicket {
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread t1 = new Thread(ticket,"A窗口");
		Thread t2 = new Thread(ticket,"B窗口");
		Thread t3 = new Thread(ticket,"C窗口");
		t1.start();
		t2.start();
		t3.start();
	}
}

暂停线程执行

sleep_yield_join_stop

暂停的方法

           方法名称                                              描述
final void join()调用该方法的线程强制执行,其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行
static void sleep(long millis)使用当前正在执行的线程休眠millis秒,线程处于阻塞状态
static void yield()当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待执行的线程,这个时候当前线程就会马上恢复执行
final void stop()强迫线程停止执行。已过时。不推荐使用
package cn.xjion.pro11;
public class TestJoin implements Runnable{
	public static void main(String[] args) throws InterruptedException {
		TestJoin tj = new TestJoin();
		Thread t = new Thread(tj);
		Thread t2 = new Thread(tj);
		
		t.start();
		t2.start();
		
		for (int i = 0;i < 10;i++) {
			if (i == 3) {
				t2.join();
			}
			System.out.println("------------"+Thread.currentThread().getName()+"-------->"+i);
		}
	}
	/**
	 * 导致调用它的线程进入阻塞状态,而不会导致其它的进程
	 * */

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i < 10;i++) {
			System.out.println(Thread.currentThread().getName()+"------>"+i);
		}
	}
}
package cn.xjion.pro11;
public class TestSleep2 implements Runnable{
	/**
	 * sleep方法会导致线程进入阻塞,写哪个线程体中就会导致哪个线程进入阻塞状态
	 * @throws InterruptedException 
	 * */
	public static void main(String[] args){
		TestSleep2 ts = new TestSleep2();
		new Thread(ts).start();
		
		System.out.println("主线程开始休眠");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("主线程休眠结束");
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			System.out.println("MyThread2.run(),线程开始休眠");
			Thread.sleep(3000);
			System.out.println("MyThread2.run(),休眠结束");
		} catch (InterruptedException e) {
			System.out.println("MyThread2.run(),产生异常");
		}
	}
}
package cn.xjion.pro11;
public class TestYield implements Runnable{
	public static void main(String[] args) {
		TestYield ty = new TestYield();
		new Thread(ty).start();
		
		for (int i = 0;i < 10;i++) {
			if (i == 5) {
				Thread.yield();//主线程礼让一次
				System.out.println(Thread.currentThread().getName()+"线程礼让一次");
			}
			System.out.println(Thread.currentThread().getName()+"------------"+i);
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0;i < 10;i++) {
			if (i == 3) {
				Thread.yield();
				System.out.println("当前线程:"+Thread.currentThread().getName()+"线程礼让一次");
			}
			System.out.println("i="+i);
		}
	}
}
package cn.xjion.pro11;
public class TestStop implements Runnable{
	public static void main(String[] args) {
		TestStop ts = new TestStop();
		Thread t = new Thread(ts);
		t.start();
		for (int i = 0;i<10;i++) {
			if (i==3) {
				t.stop(); //已过时,不建议使用
			}
			System.out.println(Thread.currentThread().getName()+"--->"+i);
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0;i < 10;i++) {
			System.out.println("i="+i);
		}
	}
}

总结
1.sleep
不会释放锁,Sleep时别的线程也不可以访问锁定对象。
2.yield
让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。
3.join
当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行。

4.stop

直接强制结束线程

线程优先级

方法

方法名称描述
final int getPriority()获取线程的优先级
final void setPriority(int priority)设置线程的优先级
package cn.xjion.pro11;
public class Test implements Runnable{
	public static void main(String[] args) {
		System.out.println("最高优先级:" + Thread.MAX_PRIORITY);
		System.out.println("最低优先级:" + Thread.MIN_PRIORITY);
		System.out.println("默认优先级" + Thread.NORM_PRIORITY);
		//主线程的优先级
		Thread t = Thread.currentThread();
		System.out.println("获取主线程的优先级:" + t.getPriority());
		
		Thread t2 = new Thread(new MyThread());
		System.out.println("新建的线程优先级为:" + t2.getPriority());
		
		/**
		 * 优先级越高越有可能先被调用执行,但是不一定
		 */
		t2.setPriority(6);
		System.out.println("t2线程的优先级:" + t2.getPriority());
		//t2.setPriority(100); //非法参数,因为优先级只能是1-10之间的整数
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
	}
}

 线程同步

通过synchronized使线程同步

锁方法:public synchronized void method(){},锁的是调用当前方法对象里面的方法

锁语句块:synchronized(对象){},锁的是传入的当前对象

package cn.xjion.pro11;
public class Ticket3 implements Runnable{
	private int ticket = 5;
	
	@Override
	public void run() {
		for (int i=0;i<100;i++) {//每个窗口排了100个人
			this.saleTicket();
		}
	}
	private synchronized void saleTicket() { //无需指定同步监视器,同步监视器只能是当前对象this
		if (ticket > 0) {//有票
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"正在卖第" + (ticket--)+"张票");
		}
	}
}

死锁及解决办法

死锁产生原因

多线程操作时,互相等待对方的资源

package cn.xjion.pro11;
public class DeadLock extends Thread{
	private Object money;
	private Object water;
	public boolean flag;//标识持有对象锁
	public DeadLock(Object money, Object water) {
		super();
		this.money = money;
		this.water = water;
	}
	
	public void run() {
		if (flag) {//true时,持有“钱”的锁
			synchronized (money) {
				System.out.println("有钱,等水");
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (water) {
					System.out.println("有水,等钱");
				}
			}
		}else {
			synchronized (water) {
				System.out.println("有水,等钱");
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (money) {
					System.out.println("有钱,等水");
				}
			}
		}
	}
}

解决死锁

package cn.xjion.pro11;
public class DeadLock2 extends Thread{
	private Object money;
	private Object water;
	public boolean flag;//标识持有对象锁
	public DeadLock2(Object money, Object water) {
		super();
		this.money = money;
		this.water = water;
	}
	
	public void run() {
		if (flag) {//true时,持有“钱”的锁
			synchronized (money) {
				System.out.println("有钱,等水");
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			synchronized (water) {
				System.out.println("有水,等钱");
			}
		}else {
			synchronized (water) {
				System.out.println("有水,等钱");
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			synchronized (money) {
				System.out.println("有钱,等水");
			}
		}
	}
}

避免死锁

银行家算法: 该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。(计算资源的大小,计算出来后,永远按照从大到小的方式来获得锁)。

多线程通信

线程通信的目的是为了能够让线程之间相互发送信号。另外,线程通信还能够使得线程等待其它线程的信号,比如,线程B可以等待线程A的信号,这个信号可以是线程A已经处理完成的信号;
Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题。

  • * wait():释放当前线程的同步监视控制器,并让当前线程进入阻塞状态,直到别的线程发出notify将该线程唤醒。
  • * notify():唤醒在等待控制监视器的其中一个线程(随机)。只有当前线程释放了同步监视器锁(调用wait)之后,被唤醒的线程才有机会执行。
  • * notifyAll():与上面notify的区别是同时唤醒多个等待线程。

值得注意的是这三个方法是属于Object而不是属于Thread的,但是调用的时候必须用同步监视器来调用,wait(), notify(), notifyAll() 必须和synchronized关键字联合使用

package cn.xjion.pro14;

import java.util.ArrayList;
import java.util.List;

public class ListAdd {
	private volatile static List list = new ArrayList<>();
	public void add(){
		list.add("xjion");
	}
	public int size(){
		return list.size();
	}
	public static void main(String[] args) {
		final ListAdd list1 = new ListAdd();
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i=0;i<10;i++){
					list1.add();
					System.out.println("当前线程:"+Thread.currentThread().getName()+"添加了一个元素");
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		},"t1");
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true){
					if (list1.size() == 5) {
						System.out.println("当前线程收到通知:"+Thread.currentThread().getName()+"list size = 5 线程停止..");
						throw new RuntimeException();
					}
				}
			}
		},"t2");
		t1.start();
		t2.start();
	}
	
}
package cn.xjion.pro14;

import java.util.concurrent.CountDownLatch;

import cn.xjion.list.ArrayList;
import cn.xjion.list.List;

public class ListAdd2 {
	private volatile static List list = new ArrayList();
	public void add(){
		list.add("xjion");
	}
	public int size(){
		return list.size();
	}
	public static void main(String[] args) {
		final ListAdd2 list2 = new ListAdd2();
		final CountDownLatch countDownLatch = new CountDownLatch(1);
		Thread t1 = new Thread(new Runnable() {
			           @Override
			           public void run() {
			             try {
			               //synchronized (lock) {
			                 for(int i = 0; i <10; i++){
			                   list2.add();
			                   System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
			                   Thread.sleep(500);
			                   if(list2.size() == 5){
			                     System.out.println("已经发出通知..");
			                     countDownLatch.countDown();
			                     //lock.notify();
			                   }
			                 }                        
			               //}
			             } catch (InterruptedException e) {
			               e.printStackTrace();
			             }
			
			          }
			       }, "t1");
			
		         Thread t2 = new Thread(new Runnable() {
			          @Override
	        public void run() {
			             //synchronized (lock) {
			               if(list2.size() != 5){
			                 try {
			                   //System.out.println("t2进入...");
			                  //lock.wait();
			                    countDownLatch.await();
			                  } catch (InterruptedException e) {
			                    e.printStackTrace();
			                  }
			               }
			             System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
			              throw new RuntimeException();
			            //}
				           }
			       }, "t2");
			
			        t2.start();
			        t1.start();
			
			      }
	}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值