Java多线程

举报
不会压弯的小飞侠 发表于 2022/08/20 12:58:33 2022/08/20
【摘要】 进程与线程进程(Process):进程是程序的一次动态执行过程,它经历了从代码加载、执行、到执行完毕的一个完整过程;同时也是并发执行的程序在执行过程中分配和管理资源的基本单位,竞争计算机系统资源的基本单位。线程(Thread):线程可以理解为进程中的执行的一段程序片段,是进程的一个执行单元,是进程内可调度实体,是比进程更小的独立运行的基本单位,线程也被称为轻量级进程。 一、多线程的实现 1...

进程与线程

进程(Process):进程是程序的一次动态执行过程,它经历了从代码加载、执行、到执行完毕的一个完整过程;同时也是并发执行的程序在执行过程中分配和管理资源的基本单位,竞争计算机系统资源的基本单位。

线程(Thread):线程可以理解为进程中的执行的一段程序片段,是进程的一个执行单元,是进程内可调度实体,是比进程更小的独立运行的基本单位,线程也被称为轻量级进程。

一、多线程的实现

1.继承Thread类

  • 方式1:继承Thread类
    • 定义一个类MyThread继承Thread类
    • 在MyThread类中重写run0方法
    • 创建MyThread类的对象
    • 启动线程

案例分析:

package com.Thread;
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(i);
        }
    }
}


package com.Thread;
public class MyThreadTest {
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
//        t1.run();
//        t2.run();
        //start()导致此线程开始执行,java虚拟机调用此线程的run方法
        t1.start();
        t2.start();
    }
}

1.设置和获取线程名称

package com.Thread;
public class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            //getName()获取线程名称
            System.out.println(getName()+":"+i);
        }
    }
}



package com.Thread;
public class MyThreadTest {
    public static void main(String[] args) {
//        MyThread t1=new MyThread();
//        MyThread t2=new MyThread();
       /* //设置线程名称(方式一)
        t1.setName("小马哥");
        t2.setName("小飞侠");*/
//        t1.run();
//        t2.run();
        //start()导致此线程开始执行,java虚拟机调用此线程的run方法
        
        //设置线程名称(方式二)
        MyThread t1=new MyThread("小马哥");
        MyThread t2=new MyThread("小飞侠");

        t1.start();
        t2.start();
    }
}


2.线程优先级

  • Thread类中设置和获取线程优先级的方法
    • public final int getPriority0):返回此线程的优先级
    • public final void setPriority(int newPriority):更改此线程的优先级

案例分析:

package com.priority;

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


package com.priority;

public class MyPriorityTest {
    public static void main(String[] args) {
        MyPriority p1=new MyPriority();
        MyPriority p2=new MyPriority();
        MyPriority p3=new MyPriority();
        p1.setName("线程一");
        p2.setName("线程二");
        p3.setName("线程三");

//        System.out.println(Thread.NORM_PRIORITY);  //5
//        System.out.println(Thread.MAX_PRIORITY);  //10
//        System.out.println(Thread.MIN_PRIORITY);  //1

        //设置线程优先级
        p1.setPriority(1);
        p2.setPriority(7);
        p3.setPriority(10);  //线程等级越高获取cpu时间片的几率高

        p1.start();
        p2.start();
        p3.start();
    }
}

3.线程控制

方法名 说明
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
void join() 等待这个线程死亡
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

案例分析:

package com.control;

public class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

package com.control;

public class ThreadSleepTest {
    public static void main(String[] args) {
        ThreadSleep s1=new ThreadSleep();
        ThreadSleep s2=new ThreadSleep();
        ThreadSleep s3=new ThreadSleep();
        s1.setName("小马哥");
        s2.setName("小飞侠");
        s3.setName("老六");
        s1.start();
        s2.start();
        s3.start();

    }
}

package com.control;

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

package com.control;

public class ThreadJoinTest {
    public static void main(String[] args) {
        ThreadJoin j1=new ThreadJoin();
        ThreadJoin j2=new ThreadJoin();
        ThreadJoin j3=new ThreadJoin();
        j1.setName("老大");
        j2.setName("老二");
        j3.setName("老三");
        j2.start();
        //当j2执行完之后,j1,j3才开始执行
        try {
            j2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        j1.start();
        j3.start();
    }
}

package com.control;

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

package com.control;

public class ThreadDaemonTest {
    public static void main(String[] args) {
        ThreadDaemon d1=new ThreadDaemon();
        ThreadDaemon d2=new ThreadDaemon();

        d1.setName("张飞");
        d2.setName("关羽");
        //设置主线程,主线程执行完毕之后,守护线程也会很快的结束
        Thread.currentThread().setName("刘备");
        //设置守护线程
        d1.setDaemon(true);
        d2.setDaemon(true);

        d1.start();
        d2.start();

        for (int i = 0; i <10 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }

    }

}

4.线程的生命周期

在这里插入图片描述

2.实现Runnable接口的方式实现多线程

  • 方式2:实现Runnable接口
    • 定义一个类MyRunnable实现Runnable接口
    • 在MyRunnable类中重写run()方法
    • 创建MyRunnable类的对象
    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    • 启动线程
  • 多线程的实现方案有两种
    • 继承Thread类
    • 实现Runnable接口
  • 相比继承Thread类,实现Runnable接口的好处
    • 避免了Java单继承的局限性
    • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好子的体现了面向对象的设计思想。

案例分析:

package com.Runnable;

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

package com.Runnable;

public class MyRunableTest {
    public static void main(String[] args) {
        MyRunnable m=new MyRunnable();
        Thread t1=new Thread(m,"小马哥");
        Thread t2=new Thread(m,"小飞侠");
        t1.start();
        t2.start();
    }
}

二、线程同步

  • 锁多条语句操作共享数据,可以使用同步代码块实现
  • 格式:
synchronized(任意对象){
     多条语句操作共享数据的代码
)
  • synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
    同步的好处和弊端
  • 好处:解决了多线程的数据安全问题
  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

案例分析:

package com.SellTicketTest;

public class SellTicket implements Runnable {
    private int ticket=100;
    private Object obj=new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + ticket + "张票");
                    ticket--;
                }
            }
        }
    }
}


package com.SellTicketTest;

public class SellTicketTest {
    public static void main(String[] args) {
        SellTicket ticket=new SellTicket();
        Thread t1=new Thread(ticket,"窗口一");
        Thread t2=new Thread(ticket,"窗口二");
        Thread t3=new Thread(ticket,"窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}

三、线程安全的类

/*
线程安全的类;
    StringBuffer
    Vector
    HashtabLe
    */
public c1ass ThreadTest {
    public static void main(string[] args){
         StringBuffer sb = new StringBuffer();
         StringBuilder sb2 = new StringBuilder();
         
         Vector<String> v = new Vector<String>();
         ArrayList<String> array = new ArrayList<string>();
        Hashtable<String,String> ht = new Hashtable<String,string>();
        HashNap<String,string> hm = new HashMap<String,string>();
        List<String> list = Collections.synchronizedList(new ArrayList<String>());
      }
 }

四、Lock锁

  • Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法
    • void lock():获得锁
    • void unlock():释放锁
  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法
    • ReentrantLock():创建一个ReentrantLock的实例

案例分析:

package com.lock;

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

public class MyLock implements Runnable {
    private int ticket=100;
    private Lock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true) {

            try {
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + ticket + "张票");
                    ticket--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
}

package com.lock;
import com.SellTicketTest.SellTicket;
public class MyLockTest {
    public static void main(String[] args) {
        MyLock ticket=new MyLock();
        Thread t1=new Thread(ticket,"窗口一");
        Thread t2=new Thread(ticket,"窗口二");
        Thread t3=new Thread(ticket,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

五、生产者消费者模式

  • ava就提供了几个方法供我们使用,这几个方法在Object类中Object类的等待和唤醒方法:
方法名 说明
void wait() 导致当前线程等待,直到另一个线程调用该对象的notify)方法或notifyAll)方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程

案例分析:

 
class Box{
	private int milk;
	private boolean state=false;
	public synchronized void put(int milk)  {//同步代码块:执行这块代码后,所在线程加锁,不会被抢占使用权。
		                                     //这时其他线程要执行,需要wait()该线程,notify()其他线程
		if(state) {  //有奶,不再继续放,put的线程暂停,等待get线程拿出奶
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//state=false,没有奶,生产者放入奶,这是第i瓶
		this.milk=milk;
		System.out.println("生产者放入第"+this.milk+"瓶奶");
		state=true;   //有了奶,奶箱不为空,修改奶箱状态
		notifyAll();  //唤醒其他所有线程(唤醒get线程,取出牛奶)
	}
	public synchronized void get()  {
		if(!state) {  //state=false,没有奶,消费者没法拿出奶,只能等待
			try {
				wait();  //消费者的get行为/线程开始等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//state=true,有奶,可以拿出,这是第i瓶奶
		System.out.println("消费者拿出第"+this.milk+"瓶奶");
		state=false; //拿出以后,box空,修改box状态
		notifyAll();  //唤醒其他所有线程(唤醒put线程,开始放入)
	}
}
 
class Producer implements Runnable{
	private Box b;
	public Producer(Box b) {
		this.b=b;
	}
	@Override
	public void run() {
		for(int i=1;i<11;i++) {
			b.put(i);
		}
	}
   
}
 
class Customer implements Runnable{
	private Box b;
	public Customer(Box b) {
		this.b=b;
    }
	@Override
	public void run() {
		while(true) {
			b.get();
		}
	}
}
 
public class Milk {
	public static void main(String[] args) {
		Box b=new Box();    //创建一个奶箱
		Producer p=new Producer(b);  //都用这个奶箱
		Customer c=new Customer(b);
		Thread t1=new Thread(p);    //producer在线程1中
		Thread t2=new Thread(c);    //customer在线程2中
		t1.start();
		t2.start();
		
	}
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。