在操作系统中运行的程序就是进程;
一个进程可以有多个线程;
程序是指令和数据的有序集合,是一个静态的概念;
而进程则是执行程序的一次执行过程,是一个动态的概念;
进程是系统资源分配的单位
线程是CPU调度和执行的单位
真正的多线程是指多个cpu,即多核
理解:进程相当于一个类,类中有多个方法(即多个线程),main()方法即(主线程),gc线程。
main()方法即(主线程)为系统的入口,用于执行整个程序;线程的运行是由调度器安排调度,不能人为干预;对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制线程会带来额外的开销,如cpu的调度时间,并发控制开销每个线程在自己的工作内存交互,内存控制不当会造成数据不一致三种创建方式
Thread class
继承Thread类Runnable接口
实现Runnable接口Callable接口
实现Callable接口多线程实现多张图片同时下载
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; public class TestThread02 extends Thread{ private String url; private String name; public TestThread02(String url,String name){ this.url=url; this.name=name; } public TestThread02(){ } @Override public void run() { //下载线程 WebDownloader webDownloader = new WebDownloader(); try { webDownloader.downloader(url,name); System.out.println(name); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { TestThread02 testThread02 = new TestThread02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603037822768&di=5f0cc79b7c104781de8cea48c7d8b1ca&imgtype=0&src=http%3A%2F%2Fattach.bbs.miui.com%2Fforum%2Fmonth_1012%2F10120514509c7244b23f4a2fa5.jpg","a.jpg"); TestThread02 testThread03 = new TestThread02("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00720-1746.jpg","a1.jpg"); TestThread02 testThread04 = new TestThread02("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2441956749,4275399915&fm=26&gp=0.jpg","a2.jpg"); testThread02.start(); testThread03.start(); testThread04.start(); } } //下载器 class WebDownloader{ //下载方法 public void downloader(String url,String name) throws IOException { FileUtils.copyURLToFile(new URL(url),new File(name)); } }推荐使用Runnable对象,因为Java单继承的局限性
定义MyRunnable类实现Runnable接口实现run()方法,编写线程执行体创建线程对象,调用start()方法启动线程 public class TestRunnable01 implements Runnable{ public void run() { for (int i = 0; i < 20; i++) { System.out.println("*******************"); } } public static void main(String[] args) { TestRunnable01 testRunnable01 = new TestRunnable01(); new Thread(testRunnable01).start(); } }继承Thread
子类继承Thread类具备多线程能力启动线程:子类对象.start()不建议使用:避免OOP单继承局限性实现Runnable接口
实现接口Runnable具备多线程能力启动线程:传入目标对象+Thread对象。start()推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用多个线程操作同一个资源问题
//多个线程操作同一个资源 //模拟买票 //发现问题:多个线程操作同一个人资源的情况下,线程不安全,数据紊乱。 public class TestRunnable02 implements Runnable{ private int ticket=10; public void run() { while (true){ if (ticket<=0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticket--+"票"); } } public static void main(String[] args) { TestRunnable02 runnable02=new TestRunnable02(); new Thread(runnable02,"小鹏").start(); new Thread(runnable02,"小强").start(); new Thread(runnable02,"小慧").start(); } }函数式接口:只有一个抽象方法的接口
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
lambda推导过程代码
import java.awt.peer.LabelPeer; /* 推到lambda表达式 */ public class TestLambda { //3.静态内部类 static class Like2 implements ILikeLamb{ public void lambda() { System.out.println("i like lambda2***"); } } public static void main(String[] args) { //方式1使用外部类 ILikeLamb like = new Like1(); like.lambda(); //方式2使用静态内部类 like=new Like2(); like.lambda(); //4.局部内部类 class Like3 implements ILikeLamb{ public void lambda() { System.out.println("i like lambda3!!!"); } } //方式3使用局部内部类 like=new Like3(); like.lambda(); //5.匿名内部类 like=new ILikeLamb() { public void lambda() { System.out.println("i like lambda4~~~~~~"); } }; //方式4使用匿名内部类 like.lambda(); //6.lambda表达式 like=() -> { System.out.println("i like lambda5~***~~"); }; //方式5使用lambda表达式 like.lambda(); } } //1.定义一个函数式接口 interface ILikeLamb{ void lambda(); } //2.实现类 class Like1 implements ILikeLamb{ public void lambda() { System.out.println("i like lambda"); } }lambda简化
class TestLambda2 { public static void main(String[] args) { //局部内部类 class Love implements ILove{ @Override public void live(int a) { System.out.println("i love number is"+a); } } ILove love=new Love(); love.live(666); //匿名内部类 love=new ILove() { @Override public void live(int a) { System.out.println("i love number is"+a); } }; love.live(888); //lambda表达式 love=(int a)-> { System.out.println("i love number is"+a); }; love.live(999); //简化1,去参数类型 love=(a)-> { System.out.println("i love number is"+a); }; //简化2.去括号 love=a-> { System.out.println("i love number is"+a); }; //简化3.去花括号 love = a -> System.out.println("i love number is"+a); //总结: //方法中只有一行代码才可以简化花括号,多行得用{}; //前提接口必须是函数式接口; //多个参数得同时都去掉类型,却得用()括起来(a,b,c); } } interface ILove{ public void live(int a); }五种状态
两张图对比理解
自定义停止代码
public class ThreadStop implements Runnable{ private boolean flg=true; @Override public void run() { int i=0; while (flg){ System.out.println("running**********"+i++); } } public static void main(String[] args) { ThreadStop threadStop=new ThreadStop(); new Thread(threadStop).start(); for (int i = 0; i < 500; i++) { System.out.println("main线程"+i); if (i==444){ threadStop.stop(); System.out.println("该线程停止了***************"); } } } public void stop(){ this.flg=false; } }模拟倒计时
//模拟倒计时 public class ThreadSleep2 implements Runnable{ private int ticket=10; @Override public void run() { while (true){ if (ticket <= 0) { break; } //模拟延时 try { Thread.sleep(1000); System.out.println(ticket--); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { ThreadSleep2 threadSleep2=new ThreadSleep2(); new Thread(threadSleep2).start(); } }模拟时钟
import java.text.SimpleDateFormat; import java.util.Date; //模拟时钟 import static java.lang.System.currentTimeMillis; public class ThreadSleep3 { public static void main(String[] args) { //获取当前系统时间 Date time= new Date(System.currentTimeMillis()); while (true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(time)); time= new Date(System.currentTimeMillis());//更新时间 } catch (InterruptedException e) { e.printStackTrace(); } } } }线程可以处于以下状态之一:
NEW 尚未启动的线程处于此状态。RUNNABLE 在Java虚拟机中执行的线程处于此状态。BLOCKED 被阻塞等待监视器锁定的线程处于此状态。WAITING 正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。TERMINATED 已退出的线程处于此状态。set和get–priority()
低优先级只意味着调度概率低,并不是优先级低就不会被先调用。
测试代码
public class ThreadPriority { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); MyThread myThread=new MyThread(); Thread thread1=new Thread(myThread); Thread thread2=new Thread(myThread); Thread thread3=new Thread(myThread); Thread thread4=new Thread(myThread); Thread thread5=new Thread(myThread); //设置优先级 thread1.setPriority(6); thread1.start(); thread2.setPriority(2); thread2.start(); thread3.setPriority(8); thread3.start(); thread4.setPriority(4); thread4.start(); thread5.setPriority(10); thread5.start(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); } }线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如,后台记录操作日志,监控内存,垃圾回收等
public class ThreadDaemon { public static void main(String[] args) { Gad gad=new Gad(); Our our=new Our(); Thread t=new Thread(gad); Thread t1=new Thread(our); t.setDaemon(true);//用户线程默认false t.start(); t1.start(); } } class Gad implements Runnable{ @Override public void run() { while (true){ System.out.println("Gad NB alone living"); } } } class Our implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("living*********living"+i); } System.out.println("death---------death=-"); } }多个线程操作同一个资源
并发:同一个对象被多个线程同时操作
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程在再使用
队列+锁:锁机制(synchronized)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJT2Wokt-1603259602918)(C:\Users\Wv587-h\AppData\Roaming\Typora\typora-user-images\image-20201020163000351.png)]
性能倒置
购票;
package safe; public class UnSafeTicket { public static void main(String[] args) { BuyTicket buy=new BuyTicket(); new Thread(buy,"小张").start(); new Thread(buy,"小五").start(); new Thread(buy,"小六").start(); new Thread(buy,"小巴").start(); } } class BuyTicket implements Runnable{ private int tickets=100; private boolean flag=true; @Override public void run() { while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { if (tickets<=0){ flag=false; return; } Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"拿到第"+tickets--+"票"); } }取款;
package safe; import com.sun.org.apache.bcel.internal.generic.NEW; import javax.naming.Name; public class UnSafeBank { public static void main(String[] args) { Account account= new Account("联邦",9999999); Bank bank=new Bank(account,5111111); Bank bank2=new Bank(account,5111111); Thread t=new Thread(bank,"多多"); t.start(); Thread t2=new Thread(bank2,"零零"); t2.start(); } } class Account{ private String account; private int money; public Account(String account, int money) { this.account = account; this.money = money; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } class Bank implements Runnable{ Account account; int drawMoney; int nowMoney; public Bank(Account account, int drawMoney) { this.account = account; this.drawMoney = drawMoney; } @Override public void run() { if (account.getMoney()-drawMoney<0){ System.out.println("余额不足!!!"); return; } try { Thread.sleep(1000);//等待让两个线程都进来 } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney()-drawMoney); nowMoney=nowMoney+drawMoney; System.out.println(Thread.currentThread().getName()+"取走了"+nowMoney); System.out.println(account.getAccount()+"卡上余额为:"+account.getMoney()); } }集合list;
package safe; import java.util.ArrayList; import java.util.List; public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list=new ArrayList<String>(); for (int i = 0; i < 10010; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(1000); System.out.println(list.size()); } }同步方法
package safe; public class SafeTicket { public static void main(String[] args) { BuyTicket1 buy=new BuyTicket1(); new Thread(buy,"小张").start(); new Thread(buy,"小五").start(); new Thread(buy,"小六").start(); new Thread(buy,"小巴").start(); } } class BuyTicket1 implements Runnable{ private int tickets=100; private boolean flag=true; @Override public void run() { while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //在方法加synchronized private synchronized void buy() throws InterruptedException { if (tickets<=0){ flag=false; return; } Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"拿到第"+tickets--+"票"); } }同步块
package safe; public class SafeBank { public static void main(String[] args) { Account1 account= new Account1("联邦",9999999); Bank1 bank=new Bank1(account,5111111); Bank1 bank2=new Bank1(account,5111111); Thread t=new Thread(bank,"多多"); t.start(); Thread t2=new Thread(bank2,"零零"); t2.start(); } } class Account1{ private String account; private int money; public Account1(String account, int money) { this.account = account; this.money = money; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } class Bank1 implements Runnable{ Account1 account; int drawMoney; int nowMoney; public Bank1(Account1 account, int drawMoney) { this.account = account; this.drawMoney = drawMoney; } @Override public void run() { //同步块,锁 变化的对象 synchronized(account){ //锁的对象就是变化的量,需要增删改 if (account.getMoney()-drawMoney<0){ System.out.println("余额不足!!!"); return; } try { Thread.sleep(1000);//等待让两个线程都进来 } catch (InterruptedException e) { e.printStackTrace(); } account.setMoney(account.getMoney()-drawMoney); nowMoney=nowMoney+drawMoney; System.out.println(Thread.currentThread().getName()+"取走了"+nowMoney); System.out.println(account.getAccount()+"卡上余额为:"+account.getMoney()); } } }对比JUC
package safe; import java.util.concurrent.CopyOnWriteArrayList; //测试JUC安全类型的集合 public class TestJUC { public static void main(String[] args) throws InterruptedException { CopyOnWriteArrayList<String> co=new CopyOnWriteArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ co.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(2000); System.out.println(co.size()); } }上面列出的四个条件,只要想办法破其中的任意一个或多个就可以避免死锁发生
从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
Lock的使用
测试代码
package safe; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { TicketLock ti=new TicketLock(); new Thread(ti).start(); new Thread(ti).start(); new Thread(ti).start(); } } class TicketLock implements Runnable{ int ticket=10; private final ReentrantLock 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(ticket--); } else { return; } } finally { lock.unlock();//解锁 } } } }synchronized与Lock的对比
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)代码
package safe; import java.util.LinkedList; public class TestPC { //最大容量 public static final int MAX_SIZE = 10; //存储媒介 public static LinkedList<Integer> list = new LinkedList<>(); class Producer implements Runnable { @Override public void run() { synchronized (list) { //仓库容量已经达到最大值 while (list.size() == MAX_SIZE) { System.out.println("仓库已满,生产者" + Thread.currentThread().getName() + "不可生产."); try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } list.add(1); System.out.println("生产者" + Thread.currentThread().getName() + "生产, 仓库容量为" + list.size()); list.notify(); } } } class Consumer implements Runnable { @Override public void run() { synchronized (list) { while (list.size() == 0) { System.out.println("仓库为空,消费者" + Thread.currentThread().getName() + "不可消费."); try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } list.removeFirst(); System.out.println("消费者" + Thread.currentThread().getName() + "消费,仓库容量为" + list.size()); list.notify(); } } } public static void main(String[] args) { TestPC pc = new TestPC(); Producer producer = pc.new Producer(); Consumer consumer = pc.new Consumer(); for (int i = 0; i < 10; i++) { Thread pro = new Thread(producer,"P"); pro.start(); Thread con = new Thread(consumer,"C"); con.start(); } } }使用线程池
代码
package thread; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPool { public static void main(String[] args) { //创建服务,创建线程池,newFixedThreadPool,参数:个数 ExecutorService service= Executors.newFixedThreadPool(5); service.execute(new MyThread1()); service.execute(new MyThread1()); service.execute(new MyThread1()); service.execute(new MyThread1()); service.execute(new MyThread1()); //关闭服务 service.shutdown(); } } class MyThread1 implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }三种方法
package thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class ThreadNew { public static void main(String[] args) throws ExecutionException, InterruptedException { //1 Thread1 t1=new Thread1(); t1.start(); //2. Thread2 t2=new Thread2(); new Thread(t2).start(); FutureTask<Integer> task=new FutureTask<Integer>(new Thread3()); new Thread(task).start(); Integer integer=task.get(); System.out.println(integer); } } //1.继承Thread class Thread1 extends Thread{ public void run(){ System.out.println("Thread111"); } } //2.实现Runnable class Thread2 implements Runnable{ @Override public void run() { System.out.println("thread222"); } } //3.实现Callable class Thread3 implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("thread3333"); return 100; } }