多线程
进程(概念):进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体。
线程(概念):线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
进程与线程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
实现方法:Thread 类
实现步骤:
1.定义一个MyThread 继承 Thread类
package com.thread;
import com.sun.org.apache.xpath.internal.objects.XString;
public class MyThread extends Thread{
@Override
public void run(){//在MyThread类中重写run()方法
for (int i = 0; i <100 ; i++) {
System.out.println(getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public MyThread(){}
public MyThread(String name){//重写带参构造方法,将内容传到父类的方法中,父类有此方法支持
super(name);
}
}
2.在MyThread类中重写run()方法
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
3…创建MyThread类对象
package com.thread;
public class test {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread("飞机");//这个类没有此方法,要调用父类方法
// t1.run();
// t2.run();还是单线程模式
t1.setName("火车");
t2.setName("高铁");
//t1.setPriority(1);
//t2.setPriority(2);
//t3.setPriority(3);
//设置当前主线程名称
Thread.currentThread().setName("高铁");
t2.setDaemon(true);//守护线程,主线程挂了,这个线程也要挂
t3.setDaemon(true);
//t1.start();
/*try {
t1.join();//t1对象线程执行完才可执行其他线程
} catch (InterruptedException e) {
e.printStackTrace();
}*/
t2.start();//多线程模式,由jvm调用run方法
t3.start();
//返回当前正在执行的线程对象名称
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
4.启动线程
run()是用来封装被线程执行的代码
run()和star()区别
run():封装被线程执行的代码,直接调用,相当于普通方法调用
star():启动线程,然后由jvm调用此线程run()方法
设置和获取线程名称方法:
(1):void setName(String name):将此线程的名称更改为等于参数na me
//命名线程方法1
public MyThread(String name){//在MyThread类中重写带参构造方法,将内容传到父类的方法中,父类有此方法支持
super(name);
}
MyThread t3 = new MyThread("飞机");//这个类没有此方法,要调用父类方法
----------------------------
//命名线程方法2
t1.setName("火车");
t2.setName("高铁");
(2):String getName():返回此线程的名称
//获取系统当前名称(main)
System.out.println(Thread.currentThread().getName()+i)
//获取t2线程名称
t2.getName();
线程调度:
(1):分时调度模型:所有线程轮流使用cpu,平均分配每个线程占用CPU时间
(2):抢占式调度模式:优先让优先级高的线程使用cpu,如此线程优先级相同,会随机选择一个,优先级高度线程获取的CPU的时间片相对多一些
【1】public final int getPriority():返回此线程的优先级
【2】:public final void setPriority(int newPriority):更改此线程的优先级(1-10)
优先级默认是5,优先级高仅仅代表线程获取CPU时间片的几率高–>具体操作系统了解
t1.setPriority(1);
t2.setPriority(2);
t3.setPriority(3);
线程控制
(1)static void sleep(long milis):使当前正在执行的线程停留的毫秒数
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
(2):void join():等待这个线程死亡
t1.start();
try {
t1.join();//t1对象线程执行完才可执行其他线程
} catch (InterruptedException e) {
e.printStackTrace();
}
(3):void setDaemon(boolean on):将此线程标记为守护线程,当运行的线程都是守护线程时,java虚拟机将退出。
//设置当前主线程名称
Thread.currentThread().setName("高铁");
t2.setDaemon(true);//守护线程,主线程挂了,这个线程也要挂
t3.setDaemon(true);
线程生命周期
Runnable类
(
实现方法:Runnable 类
实现步骤:
1.定义一个MyRunnable实现Runnable 类
package Runnable;
public class MyRunnable implements Runnable{
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
2.在MyRunnable 类中重写run()方法
@Override
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
3…创建MyRunnable 类对象
package Runnable;
public class test1 {
public static void main(String[] args) {
MyRunnable m1 = new MyRunnable();
MyRunnable m2 = new MyRunnable();
Thread t1 =new Thread(m1,"高铁");
Thread t2 =new Thread(m1,"高铁");
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
4.创建Thread类对象,把MyRunnable 对象作为构造方法的参数
Thread t1 =new Thread(m1,"高铁");
Thread t2 =new Thread(m1,"高铁");//对象和更改线程名称
4.启动线程
好处:
避免java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码,数据有效分离。---->
售票系统:多个相同程序的代码去处理同一个资源的情况
package com.ticket;
public class SellTicket implements Runnable{
int poll = 100 ;
@Override
public void run() {
while (true){//死循环
if (poll>0){
System.out.println(Thread.currentThread().getName()+"正在出售"+poll+"张票");
poll--;
}
}
}
}
做一个类的实例化,创一个进程,相当于一个整体资源, //通过不同线程(窗口)去使用资源
package com.ticket;
import java.lang.Runnable;
public class SellTicketDemo {
public static void main(String[] args) {
//创一个进程,相当于一个资源
SellTicket s1 = new SellTicket();
//通过不同线程(窗口)去使用资源
Thread t1 = new Thread( s1,"窗口1");
Thread t2 = new Thread( s1, "窗口2");
Thread t3 = new Thread(s1, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
???
上述方法解决了一个资源被多个线程去访问,但问题是一个线程占用CPU了,输出正在买票,但是还没到执行到poll–,线程2就抢占了CPU,继续输出买票,结构就是一张票买了两次。
解决方案:
将操作共享代码锁起来,买票和票数减少为一个同步代码块,必须一次性执行。
同步代码块格式:(加锁)
synchronized(任意对象){
多条语句操作共享代码
}
package com.ticket;
public class SellTicket implements Runnable {
int poll = 100;
private Object obj = new Object();//创建一个对象
@Override
public void run () {
while (true) {
synchronized (obj){//上锁,obj为一个对象
if (poll > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + poll + "张票");
poll--;
}
}
}
}
}
在方法上面加锁
synchronized 返回值类型 方法名(方法参数){}
public synchronized void sellTicket()[//synchronized锁对象为this
if (poll > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + poll + "张票");
poll--;
}
}
synchronized方法上面的锁对象为this,所以要求上锁对象统一的,其他上锁地方使用synchronized(任意对象){
多条语句操作共享代码
}的任意对象为this。
同步静态方法:
修饰符 static synchronized 返回值类型 方法名(方法参数){}
synchronized方法上面的锁对象为类名.class,所以要求上锁对象统一的,其他上锁地方使用synchronized(任意对象){
多条语句操作共享代码
}的任意对象为类名.clas
Lokc锁
方法:void lock():上锁
void unlock() :释放锁
private Lock obj = new ReentrantLock();//创建一个锁对象,Lock接口不能实例化
package com.ticket;
public class SellTicket implements Runnable {
int poll = 100;
private Lock obj = new ReentrantLock();//创建一个锁对象
@Override
public void run () {
while (true) {
look.lock();//加锁
if (poll > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售" + poll + "张票");
poll--;
}
lock.unlock();//释放锁
}
}
消费者和生产者关系
系统中有一组生产者进程和一组消费者进程,生产者每次生产一个产品放入缓冲区,消费者每次从缓冲区取出一个产品并使用(注:这里的产品可以理解成某种数据)。
条件:生产者、消费者共享一个初始化为空的、大小为n的缓冲区。
int buffer;
Semaphore sp = 1,sc = 0;//信号量
process producer(void)
{
while(1)
{
{生产一个产品producer};
p(sp);
Buffer = product;
v(sc); //唤醒消费者
}
}
process consumer(void)
{
while(1)
{
p(sc);
{取走缓冲器中的产品};
v(sp); //唤醒生产者
{消费该产品};
}
}
上述详情操作系统
java中实现:
方法:(1)void wait():使某个线程等待
(2):void notify():唤醒等待中的单个线程
(3):void notifyAll();唤醒等待中的所有线程
流程:
苹果类(Apple)::定义成员变量,表示第几个苹果,提供生产苹果和拿苹果操作
生产者:(Produce):实现Runnable接口,重写run()方法,调用生产苹果操作
消费者(Customer):实现Runnable接口,重写run()方法,调用拿苹果操作
测试类:(Appletest):
(1)创建苹果对象,共享区域
(2)创建生产者对象,把苹果对象作为构造方法参数传递
(3)创建生产者消费对象,把苹果对象作为构造方法参数传递
(4)创建2个线程对象,把生产着和消费者对象作为构造方法参数传递
(5):启动线程
苹果类(Apple):
解释:当t1和t2线程开始竞争,假设t1首先抢到CPU,开始生产,调用生产函数,起初是false,不需要等待,生产了一个苹果,改信号量为true,然后继续开始抢CPU,假如就是t1抢到,则t1进行等待中,自然CPU就执行第二个线程t2,然后吃了一个苹果,改信号量,然后唤醒所有线程。此时信号量为false,当t2继续抢到CPU,他只有等待的份。
package com.Produce_Customer;
public class Apple {
private int num ;//苹果数量
private boolean state =false;//互斥信号量
public synchronized void put(int num){//上锁让整个函数一次性执行完
if (state) {//没有苹果,跳过wait开始生产
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.num = num;
System.out.println("生产了"+this.num+"苹果");
state= true;//生产之后就改变状态量,表示有苹果
notifyAll();//唤醒所有线程
}
public synchronized void get(){
if (!state){//没有苹果,就等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("吃了"+this.num+"苹果");
state=false;
notifyAll();
}
}
生产者:(Produce)
package com.Produce_Customer;
public class Produce implements Runnable{
private Apple a;
public Produce(Apple a) {//构造方法,测试类需要
this.a=a;
}
public void run(){
for (int i = 1; i < 5; i++) {
a.put(i);//生产4个苹果
}
}
}
消费者(Customer):
package com.Produce_Customer;
public class Customer implements Runnable {
private Apple a;
public Customer(Apple a) {
this.a =a;
}
public void run(){
while (true){
a.get();
}
}
}
测试类:
package com.Produce_Customer;
public class Appletest {
public static void main(String[] args) {
Apple a = new Apple();//一个资源,
Produce p = new Produce(a);//生产者加入了这个资源
Customer c =new Customer(a);//
Thread t1 = new Thread(p);//生产者线程
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}