欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程学习总结

程序员文章站 2022-05-31 14:17:56
...

线程学习总结

一、线程与进程

进程:

就是正在运行的程序
进程是系统进行资源分配和调用的独立单位。 每一个进程都有自己的内存空间和系统资源

线程:

在同一个进程内又可以执行多个任务,而这每一个任务我们就可以认为是一个线程
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本的单位
单线程:程序只有一条执行路径。
多线程:程序有多条执行路径。

线程的创建

线程的创建有两种方式。

1.继承Thread类
步骤:
1.自定义一个类去继承于Thread
2.在自定义类重写run()方法
为什么是run()方法:run()才是线程运行的核心方法
不是类中的所有代码都需要被线程所执行。
而这个时候,为了区分哪些代码能被线程所执行,java提供了Thread类中的run()用来包含那些线程执行的代码

例子:

public class ThreadTest extends Thread{

	private String name;
	public ThreadTest(String name) {
		this.name = name;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 20; i++) {
			System.out.println(name+i);
		}
	}
}

public class Test1 {

	public static void main(String[] args) {

		ThreadTest t = new ThreadTest("琪琪");	
		ThreadTest t2 = new ThreadTest("呵呵");
		ThreadTest t3 = new ThreadTest("ergou");
		
		t.start();
		t2.start();
		t3.start();		
	}
}

2.实现Runnable接口

步骤:
A.自定义类MyRunnable实现Runnable接口
B.重写run()方法
C.创建MyRunnable类的对象
D.创建Thread类的对象,并把步骤C的对象作为构造参数传递
例子:

public class MyRunnable implements Runnable{

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

public class MyRunnableDemo {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        MyRunnable my = new MyRunnable();
        
        Thread t1 = new Thread(my, "李四");
        t1.start();
        
        Thread t2 = new Thread(my, "张三");
        t2.start();        
	}
}

二、线程的优先级

  • 目前我们的线程是没有优先级的,肯定是有一个默认优先级,
    那么,默认优先级是多少呢?
    如何获取线程对象的优先级呢?
    public final int getPriority():返回线程对象的优先级
    如何设置线程对象的优先级呢?

     public final void setPriority(int newPriority):更改此线程的优先级。 
    
  • 注意:
    线程的默认优先级:5
    线程优先级的范围是:1-10
    线程优先级高仅仅表示线程获取CPU时间片的几率高,但是要在次数比较多,或者多运行的时候才能看到比较效果。

三、线程的礼让

  • public static void yield():暂停当前正在执行的线程对象,并执行其他的线程。
    让多个线程的执行更加和谐,但是不能保证一人一次

四、线程安全

线程安全:由于统一进程下的多个线程是共享同样的地址空间和数据的,又由于线程执行顺序的不可预知性,一个线程可能会修改其他线程正在使用的变量,所有由此产生了线程安全的问题了。那么我们该怎么解决这个问题呢?

基本思想:让程序间没有安全问题的环境
怎么实现呢?
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
(把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行)。
Java给我们提供了:同步机制。

同步代码块:
synchronized(对象){
//需要同步的代码
}
A:对象是什么呢?
我们可以随便创建一个对象试试
B:需要同步的代码是哪些呢?
把多条语句操作共享数据的代码的部分包起来

注意:
同步可以解决线程安全问题的根本原因就在那个对象上,该对象如同锁的功能。
多个线程必须是同一把锁。

同步的特点:

  • 前提:
    多个线程
  • 解决问题的时候需要注意:
    多个线程使用是同一个锁对象。
  • 同步的好处:
    同步的出现可以解决了多线程的安全问题
  • 同步的缺点:
    当线程相当多时,因为每个线程都会去判断同步上的锁,这是很浪费资源的,无形中会降低了程序的运行效率。

例子:三个窗口同时售100张票。

public class SellTicket implements Runnable{

	private  int tickes=1000;
	private Object obj = new Object();
	@Override
	public  void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized(this){
			if(tickes>0){
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickes--)+"张票");
			}else{
				break;
			}
			}
		}
	}
}

public class SellTicketDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		 SellTicket ss = new SellTicket();
			Thread t1 = new Thread(ss, "窗口1");
			Thread t2 = new Thread(ss, "窗口2");
			Thread t3 = new Thread(ss, "窗口3");
			t1.start();
			t2.start();
			t3.start();
	}
}

五、Lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到
它哪里上了锁和哪里释放了锁。
为了更清晰的表达如何加锁和放锁,JDK5以后提供了一个新的锁对象Lock。

Lock:
void lock():获取锁 上锁
void unlock():释放锁 放锁
例子:还是上面售票为例

public class SellTicket implements Runnable{

	private  int tickes=100;
	//因为static所修饰的变量 只会初始化一次。
	
	//定义一个锁对象
	private Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
		
			try {
				lock.lock();
				if(tickes>0){
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickes--)+"张票");
				}else{
					break;
				}
			} finally {
				// TODO: handle finally clause
				lock.unlock();
			}	
		}
	}
}

public class SellTickerDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		 SellTicket ss = new SellTicket();
			Thread t1 = new Thread(ss, "窗口1");
			Thread t2 = new Thread(ss, "窗口2");
			Thread t3 = new Thread(ss, "窗口3");
			t1.start();
			t2.start();
			t3.start();
	}
}
  • 分析:生产与消费
  • 资源类:Phone
  • 生产类:SetPhone (生产者)
  • 消费类:GetPhone (消费者)
  • 测试类:PhoneDemo

结果输出:null,0

  • 为什么会出现这种问题呢?
    原因:我们在每个线程都创建了新的资源,而我们要求的时候设置和获取的资源应该同一个。

  • 如何解决这个问题呢?
    用到线程同步

  • 新的问题:虽然现在线程安全了,但是呢,现在是一次一大片不要看,我就想依次的一次一个的输出 Thread.sleep()肯定是不行的

  • 如何实现呢?
    通过Java提供的等待唤醒的机制去解决

  • 等待唤醒:
    类中提供了三个方法:
    wait();等待
    notify();唤醒单个线程
    notifyAll();唤醒所有线程

  • 为什么这些方法不定义Thread类中呢?
    这些方法的调用必须通过锁对象来调用,而我们之前所使用的锁对象都是任意的。
    所以,这些方法必须定义在Object。

线程1:将数据进行锁定 p
解除p
线程2: 线程1和2去抢占资源

利用等待唤醒机制:
其实就是为了防止的数据大批量重复出现。

我的SetPhone和GetPhone的锁对象都是Phone,那么我们又学过了Java三大特性,从而可以知道我们是可以抽取“公因式”

最终版本:
把Phone的成员变量进行私有化,
把设置和获取的操作给封装为功能到Phone,并加上同步
设置或获取的线程里只需要调用同步方法即可。

代码如下:

/*生产者*/
public class SetPhone implements Runnable{

	private Phone p;
	private int x;
	public SetPhone(Phone p){
		this.p =p;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			if(x%2==0){
				p.set("华为", 2600);
			}else{
				p.set("魅族", 1100);
			}
			x++;
		}
	}
}

/*消费者*/
public class GetPhone implements Runnable{
	private Phone p;
	
	public GetPhone(Phone p){
		this.p = p;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			Phone phone = p.get();
			System.out.println(p.getName()+","+p.getPrice());
		}
	}
}

/*手机*/
public class Phone {
private int bianhao;
private String name;
private int price;
private boolean flag;//默认情况是没数据,如果是true,说明有数据

public int getBianhao() {
	return bianhao;
}

public void setBianhao(int bianhao) {
	this.bianhao = bianhao;
}

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

public int getPrice() {
	return price;
}

public void setPrice(int price) {
	this.price = price;
}

public boolean isFlag() {
	return flag;
}

public void setFlag(boolean flag) {
	this.flag = flag;
}

//设置的方法
public synchronized void set(String name,int price){
	if(this.flag){//false(不走) //false
		try {
			this.wait();//线程1等着,释放锁   
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	this.name = name;
	this.price =price;
	
	this.flag = true;
	this.notify();
}

//获取的方法
public synchronized Phone get(){
	if(!this.flag){
		try {
			this.wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	this.flag =false;
	this.notify();
	return this;
}
}

/*测试类*/
public class PhoneDemo {

public static void main(String[] args) {
	// TODO Auto-generated method stub
	
		Phone p = new Phone();
		
		SetPhone sp = new SetPhone(p);			
		GetPhone gp = new GetPhone(p);
		
		Thread t1 = new Thread(sp);			
		Thread t2 = new Thread(gp);
				
		t1.start();
		t2.start();
		
}

}
相关标签: 线程