Java 多线程
1、引入
-
操作系统他的发展史:
-
手工操作
-
批处理系统
-
多道批处理
-
分时系统
-
实时系统
-
-
进程和线程:
-
进程:正在执行的程序,其实就是一块儿内存区域,内部存储着程序的资源
-
线程:程序被CPU调度的最小单位。
-
2、java多线程
-
继承Thread类,重写run方法。
-
实现Runnable接口,实现run方法。
必须:run(), start()
run(): 线程执行的时候要执行的代码 start():启动一个线程
-
继承Thread类 实现MyThread类
public class MyThread extends Thread { ? ?@Override ? ?public void run() { ? ? ? ?// 要把子线程执行的内容写在run里面 ? ? ? ?for(int i = 0; i < 1000; i ++) { ? ? ? ? ? ?System.out.println("我是子线程:" + i); ? ? ? } ? } }
线程的使用与创建
// 1. 创建线程对象 MyThread mt = new MyThread(); // 2.调用start方法启动一个线程 mt.start();
-
实现Runable接口
实现Runable中fun方法
public class MyRunnable implements Runnable { ? ?public void run() { ? ? ? ?for(int i = 0; i < 1000; i ++) { ? ? ? System.out.println("我是子线程:" + i); ? ? ? } ? } }
线程的使用与创建
// 1. 先创建Runnable类 Runnable r = new MyRunnable(); // 2.创建线程对象必须指向我的Runnable Thread td = new Thread(r); // 3.调用start方法启动一个线程 td.start();
3、线程相关操作
3.1、设置优先级
优先级高的线程,是会有一定程度的先执行的权限。但是具体是操作系统来实现的,不同的操作系统可以效果不同。 至于先执行哪个线程,还是看操作系统是怎么分的。优先级相当于只是让操作系统稍微参考一下。
public class test {
? ?public static void main(String[] args) {
? ? ? ?MyThread mt1 = new MyThread("A线程");
? ? ? ?MyThread mt2 = new MyThread("B线程");
? ? ? ?mt2.setPriority(10);
? ? ? ?mt1.setPriority(1);
? ? ? ?mt1.start();
? ? ? ?mt2.start();
? }
}
?
3.2、睡眠
睡眠函数,是让当前线程等待一段时间再去执行之后的语句。
sleep里面的单位是毫秒,下面代码中相当于是每一秒执行一次。
@Override
public void run() {
? ?while (true) {
? ? ? ?SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
? ? ? ?Date now = new Date();
? ? ? ?System.out.println(sdf.format(now));
? ? ? ?try {
? ? ? ? ? ?Thread.sleep(1000);
? ? ? } catch (InterruptedException e) {
? ? ? ? ? ?throw new RuntimeException(e);
? ? ? }
? }
}
3.3、join(主线程等待子线程)
mt.join相当于是会程序卡在这个位置,等到mt线程执行完毕之后再去执行后面的语句。
public class MyThread extends Thread {
? ?@Override
? ?public void run() {
? ? ? ?for(int i = 0; i < 1000; i ++) {
? ? ? ? ? ?System.out.println("我是子线程:" + i);
? ? ? }
? }
?
? ?public static void main(String[] args) {
? ? ? ?MyThread mt = new MyThread();
? ? ? ?mt.start();
? ? ? ?for(int i = 0; i < 100; i++) {
? ? ? ? ? ?System.out.println("------我是主线程:" + i);
? ? ? }
? ? ? ?try {
? ? ? ? ? ?mt.join();
? ? ? } catch (InterruptedException e) {
? ? ? ? ? ?throw new RuntimeException(e);
? ? ? }
? ? ? ?System.out.println("---------------------------");
? }
}
3.4、yield(让出CPU一下)
相当于是在可以把当前的cpu让出去,让别人先用一下,只是让一下,而不会一直让下去。 主要是实现一个交替执行的效果。 具体效果,主要还是看操作系统的分配。
public class MyThread extends Thread {
? ?public MyThread(String name) {
? ? ? ?super.setName(name);
? }
? ?@Override
? ?public void run() {
? ? ? ?for(int i= 0; i < 500; i++) {
? ? ? ? ? ?System.out.println(super.getName() + "--" + i);
? ? ? ? ? ?if(i % 10 == 0) {
? ? ? ? ? ? ? ?Thread.yield();
? ? ? ? ? }
? ? ? }
? }
? ?public static void main(String[] args) {
? ? ? ?MyThread mt1 = new MyThread("A线程");
? ? ? ?MyThread mt2 = new MyThread("B线程");
? ? ? ?mt1.start();
? ? ? ?mt2.start();
? }
}
3.5、interrupt(打断睡眠)
相当于是子线程因为一些原因进行睡眠操作,但是可以用interrupt来打断子线程的睡眠操作。继续执行之后的工作。
public class MyThread extends Thread{
? ?@Override
? ?public void run() {
? ? ? ?System.out.println("我要睡觉了");
? ? ? ?try {
? ? ? ? ? ?Thread.sleep(1000000000);
? ? ? } catch (InterruptedException e) {
? ? ? ? ? ?System.out.println("为什么不上睡了");
? ? ? }
? ? ? ?System.out.println("清醒了,开始工作了。");
? }
? ?public static void main(String[] args) {
? ? ? ?MyThread mt = new MyThread();
? ? ? ?mt.start();
? ? ? ?for(int i = 0; i < 1000; i ++) {
? ? ? ? ? ?System.out.println(i);
? ? ? }
? ? ? ?mt.interrupt(); // 打断正在睡眠的子线程
? }
}
4、线程同步
线程同步: 当多个线程共享同一个资源的时候,我们可以在某个一个线程访问到这个资源的时候,把这个资源暂时封锁,等待执行结束,释放这个锁,其他线程才可以进行执行,线程同步。
总结:等待其它线程释放锁,让线程变得更加安全。
例子:如果在不同线程中操作同一个资源,比如ATM机以及银行柜台同时取钱,同时查询都是有钱的,同时取钱的话会让银行赔钱。所以需要对这个公共资源进行处理,不能有多个人同时使用这个资源,就需要在使用的时候对资源进行封锁。当其他人要用的时候需要等之前的人用完才能用,这样可以避免产生问题。
封锁的方法如下:
4.1、synchronized关键字
synchronized关键字可以将整个方法上锁,只要有人使用就上锁,用完之后自动释放。
public class Account {
? ?private double blance;
? ?public Account(double blance) {
? ? ? ?this.blance = blance;
? }
? ?public synchronized void getMoney() {
? ? ? ?if(this.blance <= 0) {
? ? ? ? ? ?System.out.println("没钱了!!");
? ? ? ? ? ?return;
? ? ? }
? ? ? ?System.out.println("我要取钱了,目前还剩下:" + this.blance);
? ? ? ?this.blance -= 1000;
? ? ? ?System.out.println("取完了,还剩下:" + this.blance);
? }
}
4.2、synchronized语句
通过synchronized() {}语句将需要上所的位置上锁。 相比于synchronized关键字的话,语句比较灵活,所以用synchronized的话首推语句。
public class Account {
? ?private double blance;
? ?public Account(double blance) {
? ? ? ?this.blance = blance;
? }
? ?public void getMoney() {
? ? ? ?synchronized (this){
? ? ? ? ? ?if(this.blance <= 0) {
? ? ? ? ? ? ? ?System.out.println("没钱了!!");
? ? ? ? ? ? ? ?return;
? ? ? ? ? }
? ? ? ? ? ?System.out.println("我要取钱了,目前还剩下:" + this.blance);
? ? ? ? ? ?this.blance -= 1000;
? ? ? }
? ? ? ?System.out.println("取完了,还剩下:" + this.blance);
? }
}
4.3、手动上锁
建一个锁、lock,使用的时候上锁,用完后解锁。
public class Account {
? ?private double blance;
? ?private Lock lock = new ReentrantLock();
? ?public Account(double blance) {
? ? ? ?this.blance = blance;
? }
? ?public void getMoney() {
? ? ? ?lock.lock();
? ? ? ?if(this.blance <= 0) {
? ? ? ? ? ?System.out.println("没钱了!!");
? ? ? ? ? ?return;
? ? ? }
? ? ? ?System.out.println("我要取钱了,目前还剩下:" + this.blance);
? ? ? ?this.blance -= 1000;
? ? ? ?System.out.println("取完了,还剩下:" + this.blance);
? ? ? ?lock.unlock();
? }
}
5、死锁 (了解)
当两个线程都需要两个资源的时候,A拥有1 申请2,B拥有2 申请1,这个时候就会产生死锁。
-
注意事项:使用synchronized的时候一定要格外注意,有没有互相调用的方法被锁定,慎重使用synchronized。
死锁实例代码:
分别锁定一个资源之后申请第二个资源。资源被占用,并且也是等待资源状态。
public class ResourceObject {
? ?public static final Object obj1 = new Object();
? ?public static final Object obj2 = new Object();
}
public class DeadLock1 extends Thread{
? ?@Override
? ?public void run() {
? ? ? ?synchronized(ResourceObject.obj1) {
? ? ? ? ? ?System.out.println("锁定资源1");
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?Thread.sleep(3000);
? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ?throw new RuntimeException(e);
? ? ? ? ? }
? ? ? ? ? ?synchronized (ResourceObject.obj2) {
? ? ? ? ? ? ? ?System.out.println("锁定资源2");
? ? ? ? ? ? ? ?System.out.println("使用完毕");
? ? ? ? ? }
? ? ? }
? }
}
public class DeadLock2 extends Thread{
? ?@Override
? ?public void run() {
? ? ? ?synchronized(ResourceObject.obj2) {
? ? ? ? ? ?System.out.println("锁定资源2");
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?Thread.sleep(1000);
? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ?throw new RuntimeException(e);
? ? ? ? ? }
? ? ? ? ? ?synchronized (ResourceObject.obj1) {
? ? ? ? ? ? ? ?System.out.println("锁定资源1");
?
? ? ? ? ? ? ? ?System.out.println("使用完毕");
? ? ? ? ? }
? ? ? }
? }
}
public class Test {
? ?public static void main(String[] args) {
? ? ? ?DeadLock1 dl1 = new DeadLock1();
? ? ? ?DeadLock2 dl2 = new DeadLock2();
? ? ? ?dl1.start();
? ? ? ?dl2.start();
? }
}
6、线程的生命周期
Thread t = new Thread(); // 创建一个线程 t.start(); //开启一个线程,线程处于就绪状态
7、生产者消费者模型
是一个非常常用的模型,增加资源的利用率以及效率。
图中上半部分是一个在进行的时候另一个必须要等待。效率非常低,总会有一个是在等待。
异步,左边不用等右边,右边不用等左边。
生产者消费者模型:读取视频这一方被称为生产者,产品就是中间的视频,右边发送给公安局的就是消费者,消费的是视频。
Queue:队列,BlockingQueue阻塞队列,当队列中没有数据的时候,需要拿出数据,队列将会将程序阻塞,阻塞到有数据,队列继续工作。
AtomicInteger 线程安全的数字类型。
public class Video { // 封装一个类用来储存单一的数据,仅仅用来表示数据。
? ?private String name;
? ?public String getName() {
? ? ? ?return name;
? }
? ?public void setName(String name) {
? ? ? ?this.name = name;
? }
? ?public Video(String name) {
? ? ? ?this.name = name;
? }
}
public class ReadVideoThread extends Thread { // 生产者,读取视频
? ?// private static int i = 0; // 虽然这个语句是所有线程共享的,因为是静态的,但是不安全,因为多个线程同时i++的时候可能会产生冲突
? ?private static AtomicInteger i = new AtomicInteger(); // 线程安全的数字类型
? ?private BlockingQueue<Video> videoQueue; // 缓冲队列,用来存放读取的数据视频
? ?public ReadVideoThread(BlockingQueue videoQueue) { // 构造函数,将缓冲队列放入。
? ? ? ?this.videoQueue = videoQueue;
? }
? ?@Override
? ?public void run() {
? ? ? ?while(true) {
? ? ? ? ? ?String name = "视频" + i.incrementAndGet();
? ? ? ? ? ?Video v = new Video(name);
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?Thread.sleep(200); // 为了模拟生产和消费的时间
? ? ? ? ? ? ? ?videoQueue.put(v); // 放入队列
? ? ? ? ? ? ? ?System.out.println("发现了一个视频:" + name);
?
? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ?throw new RuntimeException(e);
? ? ? ? ? }
? ? ? }
? }
}
public class SendYellowThread extends Thread {
? ?private BlockingQueue<Video> videoQueue; ?// 缓冲队列,用来存放读取的数据视频
?
? ?public SendYellowThread(BlockingQueue<Video> videoQueue) { // 构造函数,更新当前类的缓冲队列
? ? ? ?this.videoQueue = videoQueue;
? }
? ?@Override
? ?public void run() {
? ? ? ?while(true) {
? ? ? ? ? ?try {
? ? ? ? ? ? ? ?Thread.sleep(150); // 模拟生产者消费者的时间
? ? ? ? ? ? ? ?Video video = videoQueue.take(); // 从队列中拿出一个视频数据,会自动从队列中删除
? ? ? ? ? ? ? ?System.out.println("我发送了一个视频---->" + video.getName());
? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ?throw new RuntimeException(e);
? ? ? ? ? }
?
? ? ? }
? }
}
public class Test {
? ?public static void main(String[] args) {
? ? ? ?BlockingQueue<Video> videos = new LinkedBlockingQueue<Video>();
? ? ? ?// 创建3个生产者进程
? ? ? ?ReadVideoThread rvt1 = new ReadVideoThread(videos);
? ? ? ?ReadVideoThread rvt2 = new ReadVideoThread(videos);
? ? ? ?ReadVideoThread rvt3 = new ReadVideoThread(videos);
? ? ? ?// 创建2个消费者进程
? ? ? ?SendYellowThread syt1 = new SendYellowThread(videos);
? ? ? ?SendYellowThread syt2 ?= new SendYellowThread(videos);
?
? ? ? ?// 启动5个进程
? ? ? ?rvt1.start();
? ? ? ?rvt2.start();
? ? ? ?rvt3.start();
?
? ? ? ?syt1.start();
? ? ? ?syt2.start();
? }
}
?
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!