线程学习总结

线程学习总结

一、线程与进程

进程:

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

线程:

在同一个进程内又可以执行多个任务,而这每一个任务我们就可以认为是一个线程
线程:是程序的执行单元,执行路径。是程序使用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();
		
}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值