程序(program): 是为完成特定任务、用某种语言编写的一组指令的集合。即指一 段静态的代码,静态对象。java
进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态 的过程:有它自身的产生、存在和消亡的过程。——生命周期面试
线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。安全
并行:多个CPU同时执行多个任务。好比:多我的同时作不一样的事服务器
并发:一个CPU(采用时间片)同时执行多个任务。好比:秒杀、多我的作同一件事网络
在Java之中,若是要想实现多线程的程序,那么就必须依靠一个线程的主体类(就比如主类的概念同样,表示的是一个线程的主类),可是这个线程的主体类在定义的时候也须要有一些特殊的要求,这个类能够继承Thread类或实现Runnable接口来完成定义。多线程
要想启动线程必须依靠Thread类的start()方法执行,线程启动以后会默认调用了run()方法、并发
package com.atguigu.java; /** * @author MD * @create 2020-07-10 16:58 */ // 1. class Mythread extends Thread{ // 2. @Override public void run() { for (int i = 0; i < 50 ; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ":"+i); } } } } public class ThreadTest { public static void main(String[] args) { // 3. Mythread mythread = new Mythread(); //4. mythread.start(); // 这个仍然是在main线程中执行 for (int i = 0; i < 50 ; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName() + ":"+i); } } } } /* 注意: 1. 若是本身手动调用run()方法,那么就只是普通方法,没有启动多线程模式。 2. run()方法由JVM调用,何时调用,执行的过程控制都有操做系统的CPU调度决定。 3. 想要启动多线程,必须调用start方法。 4. 一个线程对象只能调用一次start()方法启动,若是重复调用了,则将抛出以上 的异常“IllegalThreadStateException” */ // 也可使用匿名内部类简单的写 new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + ":" + i); } } }.start();
/** * 测试Thread中经常使用的方法 * 1. start():启动当前进程,而且调用当前进程的run()方法 * 2. run():一般须要重写Thread类中的此方法,将建立的线程要执行的操做声明在这个方法中 * 3. currentThread(): 静态方法,返回当前代码的线程 * 4. getName():获取当前线程的名字 * 5. setName(): 设置当前线程的名字 * 6. yield(): 释放当前cpu的执行权 * 7. join(): 在线程A中调用线程B的join(),此时线程A就进入到了阻塞的状态,直到线程B执行完成,线程A才结束阻塞状态 * 8. sleep(): 让当前的线程睡眠指定的时间 * * @author MD * @create 2020-07-10 20:34 */ class Method extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + ":" + i); if (i % 4 == 0) yield(); } } } public class ThreadMethodTest { public static void main(String[] args) { Method method = new Method(); method.setName("线程一:"); method.start(); // 给主线程命名 Thread.currentThread().setName("主线程:"); for (int i = 0; i < 100; i++) { if (i % 2 == 0) System.out.println(Thread.currentThread().getName() + ":" + i); if (i == 10) { try { method.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
线程的优先级等级app
方法:ide
注意:工具
mythread.setPriority(Thread.MAX_PRIORITY); mythread.start(); // 设置主线程的优先级,最低 Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
package com.atguigu.java; /** * @author MD * @create 2020-07-10 21:53 */ // 1. 定义子类,实现Runnable接口。 class Mthread implements Runnable{ // 2. 子类中重写Runnable接口中的run方法。 @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) System.out.println(Thread.currentThread().getName() +":"+i); } } } public class ThreadTest1 { public static void main(String[] args) { // 3. 经过Thread类含参构造器建立线程对象。 Mthread mthread = new Mthread(); // 4. 将Runnable接口的子类对象做为实际参数传递给Thread类的构造器中。 Thread t1 = new Thread(mthread); //5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法 t1.start(); // 再启动一个线程 Thread t2 = new Thread(mthread); t2.start(); } }
class MyThread implements Runnable { @Override public void run() { // 线程的主方法 // 线程操做方法 } } MyThread mt = new MyThread(); new Thread(mt).start(); // class MyThread extends Thread { @Override public void run() { // 线程的主方法 // 线程操做方法 } } MyThread mt = new MyThread(); mt.start();
要想实现多线程,必须在主线程中建立新的线程对象。Java语言使用Thread类 及其子类的对象来表示线程,在它的一个完整的生命周期中一般要经历以下的五 种状态:
多个线程执行的不肯定性引发执行结果的不稳定
多个线程对帐本的共享,会形成操做的不完整性,会破坏数据
模拟火车站售票程序,开启三个窗口售票
package com.atguigu.java; /** * @author MD * @create 2020-07-11 9:25 */ class Window implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket > 0){ System.out.println(Thread.currentThread().getName() +":卖票,票号为" + ticket); ticket--; }else { break; } } } } public class WindowTest { public static void main(String[] args) { Window w = new Window(); new Thread(w, "窗口1").start(); new Thread(w, "窗口2").start(); new Thread(w, "窗口3").start(); } }
问题的缘由:
解决办法:
在程序之中就能够经过两种方式完成:一种是同步代码块,另一种就是同步方法。最后还有新增的Lock锁
使用synchronized关键字定义的代码块就称为同步代码块,可是在进行同步的操做之中必须设置一个要同步的对象,而这个对象应该理解为当前对象:this,必须保证惟一
//同步代码块: synchronized (对象){ // 须要被同步的代码; } //synchronized还能够放在方法声明中,表示整个方法为同步方法。 //例如: public synchronized void show (String name){ …. }
操做共享数据的代码,就是须要同步的代码
共享数据:多个线程共同操做的变量
使用同步代码块解决实现Runnable接口的方式的线程安全问题
多个线程要公用一把锁,这里用的是this,由于是实现这个接口,后面就new了一个对象
/** * @author MD * @create 2020-07-11 9:25 */ class Window implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ synchronized (this){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket > 0){ System.out.println(Thread.currentThread().getName() +":卖票,票号为" + ticket); ticket--; }else { break; } } } } } public class WindowTest { public static void main(String[] args) { Window w = new Window(); new Thread(w, "窗口1").start(); new Thread(w, "窗口2").start(); new Thread(w, "窗口3").start(); } }
同步的方式,解决了线程安全的问题
可是操做同步代码的时候,只能有一个进程参与,其余的进程等待,至关于一个单线程的过程,这个时候效率低
使用同步代码块解决继承Thread类的方式的线程安全问题
这里的同步锁不能再用this了,由于后面new了多个对象
注意声明为静态的
class Window2 extends Thread { // 这里要注意声明为静态的 private static int ticket = 100; private static Object obj = new Object(); @Override public void run() { while (true) { // 也能够写成这样 // synchronized (Window2.class) synchronized (obj){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket); ticket--; } else { break; } } } } } public class WindowTest2 { public static void main(String[] args) { Window2 w1 = new Window2(); Window2 w2 = new Window2(); Window2 w3 = new Window2(); w1.setName("窗口1"); w1.start(); w2.setName("窗口2"); w2.start(); w3.setName("窗口3"); w3.start(); } }
若是操做共享数据的代码完整的声明在一个方法中,能够将这个方法声明为同步的,这个方法就是同步方法
使用同步方法解决实现Runnable接口的线程问题
将方法声明为同步就行使用synchronized
package com.atguigu.java; /** * @author MD * @create 2020-07-11 10:32 */ class Window3 implements Runnable { private int ticket = 100; @Override public void run() { while (true) { show(); } } // 直接把这个方法声明为这个就能够了 // 这个同步监视器就是this public synchronized void show(){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket); ticket--; } } } public class WindowTest3 { public static void main(String[] args) { Window3 w = new Window3(); new Thread(w, "窗口1").start(); new Thread(w, "窗口2").start(); new Thread(w, "窗口3").start(); } }
使用同步方法解决继承Thread的线程问题
将方法声明为同步使用synchronized,而且该方法为静态的使用static
package com.atguigu.java; /** * @author MD * @create 2020-07-11 10:39 */ class Window4 extends Thread { private static int ticket = 100; @Override public void run() { while (true) { show(); } } // 此时的同步锁是当前的类 private static synchronized void show(){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket); ticket--; } } } public class WindowTest4 { public static void main(String[] args) { Window4 w1 = new Window4(); Window4 w2 = new Window4(); Window4 w3 = new Window4(); w1.setName("窗口1"); w1.start(); w2.setName("窗口2"); w2.start(); w3.setName("窗口3"); w3.start(); } }
同步就是指一个线程要等待另一个线程执行完毕才会继续执行的一种操做形式,可是若是在一个操做之中都是在互相等着的话,那么就会出现死锁问题。
面试题:请问多个线程操做同一资源的时候要考虑到那些,会带来那些问题?
多个线程访问同一资源的时候必定要考虑到同步的问题,可是过多的同步会带来死锁。
package com.atguigu.java1; /** * @author MD * @create 2020-07-11 11:01 */ public class ThreadTest { public static void main(String[] args) { StringBuilder s1 = new StringBuilder(); StringBuilder s2 = new StringBuilder(); new Thread(){ @Override public void run() { synchronized (s1){ s1.append("a"); s2.append("1"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s2){ s1.append("b"); s2.append("2"); System.out.println(s1); System.out.println(s2); } } } }.start(); new Thread(new Runnable() { @Override public void run() { synchronized (s2) { s1.append("c"); s2.append("3"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s1) { s1.append("d"); s2.append("4"); System.out.println(s1); System.out.println(s2); } } } }).start(); } }
class A{ private final ReentrantLock lock = new ReenTrantLock(); public void m(){ lock.lock(); try{ //保证线程安全的代码; } finally{ lock.unlock(); } } } //注意:若是同步代码有异常,要将unlock()写入finally语句块
面试题:synchronized和Lock的异同
注意:若是是使用继承Thread的方式,lock锁得加一个static,这样才能保证是同一把锁
package com.atguigu.java1; import java.util.concurrent.locks.ReentrantLock; /** * @author MD * @create 2020-07-11 16:00 */ class Window implements Runnable{ private int ticket = 100; // 1. 实例化ReentrantLock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { // 2. 调用锁定方法lock() lock.lock(); if (ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " :售票,票号为:" + ticket); ticket--; }else { break; } }finally { //3. 调用解锁方法 unlock() lock.unlock(); } } } } public class LockTest { public static void main(String[] args) { Window w1 = new Window(); new Thread(w1,"窗口1").start(); new Thread(w1,"窗口2").start(); new Thread(w1,"窗口3").start(); } }
如何解决线程安全问题?有几种方式?
同步机制,三种方式
使用两个线程交替打印1-100
package com.atguigu.java2; /** * @author MD * @create 2020-07-11 16:44 */ class Number implements Runnable{ private int number = 1; @Override public void run() { while (true){ synchronized (this){ notify(); if (number <= 100){ System.out.println(Thread.currentThread().getName() + " : "+number); number++; // 使得调用wait()方法的线程进入到了阻塞状态 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } } public class CommunicationTest { public static void main(String[] args) { Number n = new Number(); new Thread(n,"A").start(); new Thread(n,"B").start(); } }
wait() 与 notify() 和 notifyAll()
注意
package com.atguigu.exer; /** * @author MD * @create 2020-07-11 17:14 */ class Clerk{ private int productCount = 0; // 生产产品 public synchronized void produceProduct() { if (productCount < 20){ productCount++; System.out.println(Thread.currentThread().getName() + ":开始生产第 "+productCount+"个产品"); notify(); }else{ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } // 消费产品 public synchronized void consumeProduct() { if (productCount > 0){ System.out.println(Thread.currentThread().getName() +":开始消费产品第" + productCount+"个产品"); productCount--; notify(); }else{ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 生产者 class Producer extends Thread{ private Clerk clerk; public Producer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { System.out.println(getName()+": 开始生成产品"); while (true){ clerk.produceProduct(); } } } // 消费者 class Consumer extends Thread{ private Clerk clerk; public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { System.out.println(getName()+": 开始消费产品"); while (true){ clerk.consumeProduct(); } } } public class ProdectTest { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer p1 = new Producer(clerk); p1.setName("生产者1"); p1.start(); Consumer c1 = new Consumer(clerk); c1.setName("消费者1"); c1.start(); } }
与使用Runnable相比, Callable功能更强大些
具体步骤以下:
package com.atguigu.java2; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @author MD * @create 2020-07-11 17:43 */ // 1. 建立一个实现Callable接口的实现类 class NumThread implements Callable{ //2. 实现call()方法,将此线程须要执行的操做声明在call()方法中 @Override public Object call() throws Exception { int sum = 0; for (int i = 0; i <= 100; i++) { if (i % 2 == 0){ sum += i; } } return sum; } } public class ThreadNew { public static void main(String[] args) { //3. 建立Callable接口实现类的对象 NumThread numThread = new NumThread(); // 4. 将Callable接口实现类的对象做为参数传递到FutureTask构造器中,建立FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); //5. 将FutureTask的对象做为参数传递到Threa类的构造器中,建立Thread对象,并调用start()方法 new Thread(futureTask).start(); //6. 若是须要返回值就写下面的get()方法, try { // get()返回值就是FutureTask构造器参数Callable实现重写call()的返回值 Object sum = futureTask.get(); System.out.println(sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
背景:常常建立和销毁、使用量特别大的资源,好比并发状况下的线程, 对性能影响很大。
思路:提早建立好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。能够避免频繁建立销毁、实现重复利用。相似生活中的公共交 通工具。
好处:
package com.atguigu.java2; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author MD * @create 2020-07-11 18:06 */ class NumberThread implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName()+ " : "+i ); } } } } class NumberThread1 implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName()+ " : "+i ); } } } } public class ThreadTool { public static void main(String[] args) { // 1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); // 设置线程池的属性 //2. 执行指定的线程的操做,须要提供实现Runnable接口或实现Callable接口 // 适合用于Runnable service.execute(new NumberThread()); service.execute(new NumberThread1()); // 适合用于Callable //service.submit(); // 3. 最后关闭 service.shutdown(); } }