Java第十一天笔记01——多线程编程

目录

1.进程和线程的区别

2.多线程的实现

3.多线程状态及生命周期

4.线程中的主要方法

5.线程的优先级

6.线程的高级操作

7.线程同步

8.死锁

9.Lock对象


1.进程和线程的区别

  • 每个进程都有独立的代码和数据空间,进程的切换会有很大的开销
  • 同一类线程共享代码和数据空间,每个线程有独立运行的栈和程序计数器,线程切换的开销小
     

2.多线程的实现

(1)  创建线程类
继承Thread类或实现Runnable接口
(2)  通过Thread类构造器来创建线程对象
Thread( )
Thread(Runnable target)
(3)  通过start()方法激活线程对象

例子:

(1)创建继承Thread的类

package test;

public class ThreadTest extends Thread{//基层Thread类

	@Override
	public void run() {
		for (int i = 0; i < 200; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i + " ");
		}
	}

}

(2)创建实现Runnable接口的类

package test;

public class RunnableTest implements Runnable{//实现Runnable接口

	@Override
	public void run() {
		for (int i = 0; i < 200; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i + " ");
		}
	}

}

(3)测试

package test;

public class Test {
	public static void main(String[] args) {
		ThreadTest test1 = new ThreadTest();
		test1.start();//使线程就进入就绪状态
		test1.setName("test1");//设置test1线程的名字
		
		RunnableTest runnable = new RunnableTest();
		Thread test2 = new Thread(runnable);
		test2.setName("test2");//设置test2线程的名字
		test2.start();
	}
}

3.多线程状态及生命周期

4.线程中的主要方法

 例子:

ThreadTest类:

package test;

public class ThreadTest extends Thread{//基层Thread类

	@Override
	public void run() {
		for (int i = 0; i < 200; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i + " ");
		}
	}

}

RunnableTest类:

package test;

public class RunnableTest implements Runnable{//实现Runnable接口

	@Override
	public void run() {
		for (int i = 0; i < 200; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i + " ");
		}
	}

}

Test类:

package test;

public class Test {
	public static void main(String[] args) {
		ThreadTest test1 = new ThreadTest();
		test1.start();//使线程就进入就绪状态
		test1.setName("test1");
		try {
			test1.join();//等待test1线程执行完毕,再执行其它线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		RunnableTest runnable = new RunnableTest();
		Thread test2 = new Thread(runnable);
		test2.setName("test2");
		test2.start();
		
		Thread.yield();//让出CPU,当前线程进入就绪队列等待调度
		
		RunnableTest runnable2 = new RunnableTest();
		Thread test3 = new Thread(runnable2);
		test3.setName("test3");
		test3.start();
		try {
			Thread.sleep(50);//将当前线程睡眠50毫秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

5.线程的优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照线程的优先级来决定应调度哪个线程来执行
  • Java线程的优先级用1~10的整数来表示,越小则优先级越低
  • 但是Java的优先级是高度依赖于操作系统的实现的
     

 Thread类的三个常量,表示常用的线程优先级:

  • Thread.MIN_PRIORITY :1
  • Thread.NORM_PRIORITY:5
  • Thread.MAX_PRIORITY:10

缺省时线程具有NORM_PRIORITY,通过getPriority()和setPriority()获取和设置线程优先级

package test;

public class Test {
	public static void main(String[] args) {
		ThreadTest test1 = new ThreadTest();
		test1.start();//使线程就进入就绪状态
		test1.setName("test1");
		System.out.println(test1.getPriority());//5,test1线程优先级
		test1.setPriority(Thread.MIN_PRIORITY);//设置test2线程优先级为1
		
		RunnableTest runnable = new RunnableTest();
		Thread test2 = new Thread(runnable);
		test2.setName("test2");
		test2.setPriority(Thread.MAX_PRIORITY);//设置test2线程优先级为10
		test2.start();
		
		RunnableTest runnable2 = new RunnableTest();
		Thread test3 = new Thread(runnable2);
		test3.setName("test3");
		test3.start();
		test1.setPriority(Thread.NORM_PRIORITY);//设置test3线程优先级为5
	}
}

6.线程的高级操作

Object类中线程的相关方法:
①void wait() 
导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll() 方法。
②void notify() 
唤醒在此对象监视器上等待的单个线程。
③void notifyAll() 
唤醒在此对象监视器上等待的所有线程。

例子:

 ①Bread类

package com.waitandnotify.test;

/**
 * 面包程序: 1.如果有面包那么就销售面包 2.如果没有面包那么就生产面包
 * 
 * 
 * 如果希望实现线程之间互相通知的效果: 1.线程调用的方法必须能够锁定对象的状态,可以是锁定方法,也可以是直接锁定对象 2. 有线程调用wait方法
 * 让出cpu的时间片 3.有线程调用notifyAll方法,唤醒其他等待对象所得线程 4.2和3在不同的代码块中( 线程中)调用
 */
public class Bread {

	private Boolean isHave = false;// 默认没有面包

	// 销售面包的方法
	public synchronized void sale() {
		if (isHave) {
			System.out.println(Thread.currentThread().getName() + "销售面包的方法sale()");
			// 销售完面包没了
			isHave = false;
			this.notifyAll();// 虽然唤醒所有等着面包对象的锁的人,实际真正抢到时间片的线程只有一个
		} else {
			// 销售面包的人,没有面包销售时就等着,等生产者生产面包之后通知他
			try {
				System.out.println(Thread.currentThread().getName() + "在等待对象的锁");
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "有面包了,完成sale方法");
		}
	}

	// 生产面包的方法
	public synchronized void produce() {
		// p1 p2
		if (!isHave) {
			// p1 p2
			System.out.println(Thread.currentThread().getName() + "生产面包的方法");
			// 销售完面包没了
			isHave = true;
			this.notifyAll();// 虽然唤醒所有等着面包对象的锁的人,实际真正抢到时间片的线程只有一个
		} else {
			// 生产面包的人,没有面包销售时就等着,等着销售面包之后通知他
			try {
				System.out.println(Thread.currentThread().getName() + "在等待对象的锁");
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "没有面包了,完成produce方法");
		}
	}
}

②ProductThread类

package com.waitandnotify.test;

public class ProductThread implements Runnable {

	private Bread bread;

	public ProductThread(Bread bread) {
		this.bread = bread;
	}

	@Override
	public void run() {
		for (int i = 0; i < 200; i++) {
			bread.produce();
		}
	}

}

③SaleThread类

package com.waitandnotify.test;

public class SaleThread implements Runnable{

	private Bread bread = null;
	
	public SaleThread(Bread bread) {
		this.bread = bread;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 200; i++) {
			bread.sale();
		}
	}
}

④Test测试类

package com.waitandnotify.test;

public class Test {
	public static void main(String[] args) {
		Bread bread = new Bread();
		SaleThread sale1 = new SaleThread(bread);
		ProductThread prod1 = new ProductThread(bread);
		
		Thread s1 = new Thread(sale1);
		s1.setName("销售者1");
		
		Thread p1 = new Thread(prod1);
		p1.setName("生产者1");
		s1.start();
		p1.start();
	}
}

 7.线程同步

  • 有时两个或多个线程可能会试图同时访问一个资源
  • 为了确保在任何时间点一个共享的资源只被一个线程使用,使用了synchronized关键字实现“同步” 
  • 当一个线程运行到需要同步的语句后,CPU不去执行其他线程中的、可能影响当前线程中的下一句代码的执行结果的代码块,必须等到下一句执行完后才能去执行其他线程中的相关代码块,这就是线程同步

 (1)实现线程同步的两种方式

①synchronized方法

synchronized void methodA() {  }

②synchronized语句

synchronized (Object) {
    //要同步的语句
}

(2)注意事项

 受到synchronized保护的程序代码块和方法中,要访问的对象属性必须设定为private,因为如果不设定为private,那么就可以用不同的方式来访问它,这样就达不到保护的效果了

(3)例子

Ticket类

package com.ticket.test;

public class Ticket implements Runnable {
	private int number = 100;// 一共100张票

	public synchronized void purchase() {// 购票
		if (number > 0) {
			System.out.println(Thread.currentThread().getName() + "买了票:No." + number);
			number--;
		}
	}

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			purchase();
		}
	}

	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread t1 = new Thread(ticket);
		t1.setName("小明");
		Thread t2 = new Thread(ticket);
		t2.setName("小红");
		Thread t3 = new Thread(ticket);
		t3.setName("小刚");
        //三个人同时抢票
		t1.start();
		t2.start();
		t3.start();
	}
}

8.死锁

 (1)概念

两个线程,彼此在等待对方占据的锁

(2)锁归还的几种方式

  • 基本上执行完同步的程序代码后,锁就会自动归还;
  • 用break语句跳出同步的语句块,不过这对于写在方法声明的synchronized没有作用;
  • 遇到return语句;
  • 遇到了异常;

(3)例子

 Thread1类

package com.dielock.test;

public class Thread1 extends Thread{
	Object o1 = null;
	Object o2 = null;
	
	public Thread1(Object o1, Object o2){
		this.o1 = o1;
		this.o2 = o2;
	}

	@Override
	public void run() {
		synchronized (o1) {
			try {
				Thread.sleep(500);
				synchronized (o2) {
					System.out.println("o2");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

Thread2类:

package com.dielock.test;

public class Thread2 extends Thread{

	Object o1 = null;
	Object o2 = null;
	
	public Thread2(Object o1, Object o2){
		this.o1 = o1;
		this.o2 = o2;
	}
	
	@Override
	public void run() {
		synchronized(o2){
			try {
				Thread.sleep(500);
				synchronized(o1){
					System.out.println(o1);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

Test测试类:

package com.dielock.test;

public class Test {
	
	public static void main(String[] args) {
		Object o1 = new Object();
		Object o2 = new Object();
		Thread1 t1 = new Thread1(o1, o2);
		t1.setName("t1");
		t1.start();
		Thread2 t2 = new Thread2(o1, o2);
		t2.setName("t2");
		t2.start();
	}
}

9.Lock对象

如何锁对象?

  • 不要锁方法
  • 把进入面包房要执行的工作代码放入锁对象的代码块中
  • 注意事项:如果访问对象的属性,那么属性必须是私有的
package com.ticket.test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable {
	private int num = 100;// 一共一百张票
	private Lock lock = new ReentrantLock();//创建锁对象

	@Override
	public void run() {
		lock.lock();//上锁
		for (int i = 0; i < 10; i++) {//一人买十张
			if (num > 0) {
				System.out.println(Thread.currentThread().getName() + "买了No." + num);
				num--;
			} else {
				break;
			}
		}
		lock.unlock();//解锁
	}

	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread t1 = new Thread(ticket);
		Thread t2 = new Thread(ticket);
		Thread t3 = new Thread(ticket);
		t1.setName("XiaoHong");
		t2.setName("XiaoLiu");
		t3.setName("XiaoXin");
		t1.start();
		t2.start();
		t3.start();
	}
}

输出结果:

XiaoHong买了No.100
XiaoHong买了No.99
XiaoHong买了No.98
XiaoHong买了No.97
XiaoHong买了No.96
XiaoHong买了No.95
XiaoHong买了No.94
XiaoHong买了No.93
XiaoHong买了No.92
XiaoHong买了No.91
XiaoLiu买了No.90
XiaoLiu买了No.89
XiaoLiu买了No.88
XiaoLiu买了No.87
XiaoLiu买了No.86
XiaoLiu买了No.85
XiaoLiu买了No.84
XiaoLiu买了No.83
XiaoLiu买了No.82
XiaoLiu买了No.81
XiaoXin买了No.80
XiaoXin买了No.79
XiaoXin买了No.78
XiaoXin买了No.77
XiaoXin买了No.76
XiaoXin买了No.75
XiaoXin买了No.74
XiaoXin买了No.73
XiaoXin买了No.72
XiaoXin买了No.71

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值