• java基础之多线程


    java基础之多线程

    1. 多线程概述

    1.1 线程与进程

      进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且每个进程都具有一定独立功能。
      线程:线程是进程中的一个执行单元,来完成进程中的某个功能。负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
      简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

    1.2 程序运行原理

    • 分时调度
      所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
    • 抢占式调度
      优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度

    2. 线程的创建和启动

    2.1 Thread类

      继承Thread类创建线程

    • 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程要完成的任务。因此把run()方法称为线程执行体。
    • 创建Thread子类的实例,即创建线程对象。
    • 调用线程对象的start()方法来启动线程。
    public class ThreadTest extends Thread{
    
    	private int i;
    	public void run() {
    		for(i=0;i<50;i++) {
    			//当线程继承Thread类的时候,直接调用this即可获取当前的进程
    			//Thread类的getName()方法会返回线程的名字
    			System.out.println(getName()+" "+i);
    		}
    	}
    	public static void main(String[] args) {
    		for(int i =0;i<50;i++) {
    			System.out.println(Thread.currentThread().getName()+" "+i);
    			if(i==10) {
    				//创建第一个线程
    				new ThreadTest().start();
    				//创建第二个线程
    				new ThreadTest().start();
    			}
    		}
    	}
    }
    
    

    2.2 Runnable接口

      实现Runnable接口创建线程类

    • 定义Runnable接口的实现类,并重写run()方法。
    • 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread方法。
    • 调用线程的start()方法来启动对象。
    public class RunnableTest implements Runnable {
    	private int i;
    	@Override
    	public void run() {
    		for(i=0;i<50;i++) {
    			System.out.println(Thread.currentThread().getName()+" "+i);
    		}
    	}
    
    	public static void main(String[] args) {
    		for(int i =0;i<50;i++) {
    			System.out.println(Thread.currentThread().getName()+" "+i);
    			if(i==10) {
    				RunnableTest rs = new RunnableTest();
    				new Thread(rs).start();
    				new Thread(rs).start();
    			}
    		}
    	}
    }
    
    

      实现Runnable的原理和好处

    • 程序设计遵循的原则:开闭原则,对修改关闭,对扩展开放,减少线程本身和任务之间的耦合性。
    • 实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
    • 继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,又有线程任务。
    • 实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

    2.3 匿名内部类

      使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

    • 方式1:创建线程对象时,直接重写Thread类中的run方法
    new Thread() {
    	public void run() {
    		for (int i = 0; i < 10; i++) {
    			System.out.println(getName() + " " + i);
    		}
    	}
    }.start();
    
    • 方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
    new Thread(new Runnable() {
    	public void run() {
    		for (int i = 0; i < 10; i++) {
    			System.out.println(Thread.currentThread().getName() + " " + i);
    		}
    	}
    }).start();
    

    2.4 多线程常用方法

    • 获取名字
      通过getName()方法获取线程对象的名字
    • 设置名字
      • 通过构造函数可以传入String类型的名字
      new Thread("yyy") {
      	public void run() {
              //do something...
      	}
      }.start(); 
      
      • 通过setName(String)方法可以设置线程对象的名字
      new Thread() {
      	public void run() {
      	    this.setName("abc");
      		for(int i = 0; i < 100; i++) {
      			System.out.println(this.getName() + "....bb");
      		}
      	}
      }.start(); 
      
    • 获取当前线程对象
      通过Thread.currentThread(), 主线程也可以获取
    • 休眠线程
      Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒
    • 守护线程
      setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
    Thread t1 = new Thread() {
    	public void run() {
    		for(int i = 0; i < 50; i++) {
    			System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    };
    Thread t2 = new Thread() {
    	public void run() {
    		for(int i = 0; i < 2; i++) {
    			System.out.println(getName() + "...bb");
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    };
    
    t1.setDaemon(true);						//将t1设置为守护线程
    
    t1.start();
    t2.start();
    //运行结果
    Thread-1...bb
    Thread-0...aaaaaaaaaaaaaaaaaaaaaa
    Thread-0...aaaaaaaaaaaaaaaaaaaaaa
    Thread-1...bb
    Thread-0...aaaaaaaaaaaaaaaaaaaaaa
    
    
    • 加入线程
      • join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
      • join(int), 可以等待指定的毫秒之后继续
      final Thread t1 = new Thread() {
      	public void run() {
      		for(int i = 0; i < 100; i++) {
      			System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
      			
      		}
      	}
      };
      Thread t2 = new Thread() {
      	public void run() {
      		for(int i = 0; i < 100; i++) {
      			System.out.println(getName() + "...bb");
      			try {
      				t1.join();
      			} catch (InterruptedException e) {
      				e.printStackTrace();
      			}
      		
      		}
      	}
      };
      t1.start();
      t2.start();
      

    2.5 同步代码块和同步方法

    • 同步代码块
        当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时就需要同步。使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块。多个同步代码块如果使用相同的锁对象,那么他们就是同步的。如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码。
    public class ThreadT {
    
    	public void print1() {
    		synchronized (ThreadT.class) {
    			System.out.print("1");
    			System.out.print("2");
    			System.out.print("3");
    			System.out.print("
    ");
    		}
    
    	}
    
    	public void print2() {
    		synchronized (ThreadT.class) {
    			System.out.print("a");
    			System.out.print("b");
    			System.out.print("c");
    			System.out.print("
    ");
    		}
    
    	}
    
    	public static void main(String[] args) {
    		ThreadT tT = new ThreadT();
    		Thread t1 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print1();
    				}
    			}
    		};
    		Thread t2 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print2();
    				}
    			}
    		};
    		t1.start();
    		t2.start();
    	}
    }
    
    
    • 同步方法
        使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的。
    public class ThreadT {
    
    	public synchronized void print1() {
    		System.out.print(Thread.currentThread().getName()+"----");
    		System.out.print("1");
    		System.out.print("2");
    		System.out.print("3");
    		System.out.print("
    ");
    	}
    
    	public synchronized void print2() {
    		System.out.print(Thread.currentThread().getName()+"----");
    		System.out.print("a");
    		System.out.print("b");
    		System.out.print("c");
    		System.out.print("
    ");
    	}
    
    	public static void main(String[] args) {
    		ThreadT tT = new ThreadT();
    		Thread t1 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 1000; i++) {
    					tT.print1();
    				}
    			}
    		};
    		Thread t2 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 1000; i++) {
    					tT.print2();
    				}
    			}
    		};
    		t1.start();
    		t2.start();
    	}
    }
    //输出结果(一部分)
    Thread-0----123
    Thread-0----123
    Thread-1----abc
    Thread-1----abc
    Thread-1----abc
    Thread-0----123
    Thread-0----123
    

    2.6 线程间通信

      多个线程并发执行时,在默认情况下CPU是随机切换线程的。如果我们希望他们有规律的执行,就可以使用通信,例如每个线程执行一次打印。
      如果希望线程等待,就调用wait(),如果希望唤醒等待的线程,就调用notify();这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。notifyAll()方法是唤醒所有线程,JDK5之前无法唤醒指定的一个线程,如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件。

    public class ThreadT {
    	
    	private int flag = 1;
    
    	public synchronized void print1() {
    		try {
    		    /*
    			if (flag != 1) {    //if 语句是在那里等待就在那里起来
    				this.wait();
    			}*/
    			while(flag != 1){
    			    this.wait();    //while循环是循环判断,每次都会判断标记
    			}
    			System.out.print("1");
    			System.out.print("2");
    			System.out.print("3");
    			System.out.print("
    ");
    			flag = 2;
    			//this.notify();
    			this.notifyAll();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public synchronized void print2() {
    		try {
    			while(flag != 2){
    			    this.wait();  
    			}
    			System.out.print("a");
    			System.out.print("b");
    			System.out.print("c");
    			System.out.print("
    ");
    			flag = 3;
    			this.notifyAll();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    	public synchronized void print3() {
    		try {
    			while(flag != 3){
    			    this.wait();  
    			}
    			System.out.print("+");
    			System.out.print("-");
    			System.out.print("*");
    			System.out.print("
    ");
    			flag = 1;
    			this.notifyAll();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static void main(String[] args) {
    		ThreadT tT = new ThreadT();
    		Thread t1 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print1();
    				}
    			}
    		};
    		Thread t2 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print2();
    				}
    			}
    		};
    		Thread t3 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print3();
    				}
    			}
    		};
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    //输出结果(一部分)
    ......
    abc
    +-*
    123
    abc
    +-*
    123
    ......
    

    2.7 互斥锁

      使用ReentrantLock类的lock()和unlock()方法进行同步。使用Condition的await()和signal()来暂停和唤醒线程

    public class ThreadT {
    
    	private int flag = 1;
    	private ReentrantLock rlock = new ReentrantLock();
    	private Condition c1 = rlock.newCondition();
    	private Condition c2 = rlock.newCondition();
    	private Condition c3 = rlock.newCondition();
    
    	public void print1() {
    		rlock.lock();   //获得锁
    		try {
    			while (flag != 1) {
    				c1.await();  //使当前线程等待,直到发出信号或中断
    			}
    			System.out.print("1");
    			System.out.print("2");
    			System.out.print("3");
    			System.out.print("
    ");
    			flag = 2;
    			c2.signal();    //唤醒等待线程。 
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} finally {
    			rlock.unlock();   //释放锁
    		}
    	}
    
    	public void print2() {
    		rlock.lock();
    		try {
    			while (flag != 2) {
    				c2.await();
    			}
    			System.out.print("a");
    			System.out.print("b");
    			System.out.print("c");
    			System.out.print("
    ");
    			flag = 3;
    			c3.signal();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} finally {
    			rlock.unlock();
    		}
    	}
    
    	public void print3() {
    		rlock.lock();
    		try {
    			while (flag != 3) {
    				c3.await();
    			}
    			System.out.print("+");
    			System.out.print("-");
    			System.out.print("*");
    			System.out.print("
    ");
    			flag = 1;
    			c1.signal();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} finally {
    			rlock.unlock();
    		}
    	}
    
    	public static void main(String[] args) {
    		ThreadT tT = new ThreadT();
    		Thread t1 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print1();
    				}
    			}
    		};
    		Thread t2 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print2();
    				}
    			}
    		};
    		Thread t3 = new Thread() {
    			public void run() {
    				for (int i = 0; i < 100; i++) {
    					tT.print3();
    				}
    			}
    		};
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    
  • 相关阅读:
    Vue父子组件传值$parent , ref,$refs,props大总结
    Vue-axios
    tensorflow2知识总结(杂)---1、安装tensorflow
    线性回归和逻辑回归的区别
    %matplotlib inline的含义
    王阳明的心学到底是啥
    Windows Anaconda 修改为国内源
    Reporting Services Single Sign On (SSO) Authentication
    .NET
    WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for 'jquery'. Please add a ScriptResourceMapping named jquery(case-sensitive)
  • 原文地址:https://www.cnblogs.com/huizhipeng/p/10108331.html
Copyright © 2020-2023  润新知