javase_19(多线程)
进程:
进程是具有一定独立功能的程序关于某个数据集合上的一次运动.
线程:
线程是进程里面的一个实体,是CPU调度或分派的基本单位
进程与线程之间的区别:
总而言之,一个进程里面可以有多个线程,但是一个进程至少有一个线程
多线程存在的意义:
程序允许有多个线程并发,提供程序的运行的效率
线程的创建方式:
new Thread().start();
多线程的应用:
Sun公司为我们提供了Thread类来实现线程
通过new Thread()来创建一个线程对象.
定义一个类继承Thread类,子类就有了线程的功能
创建Thread类的子类的对象,也可以创建一个线程.
线程的启用:
Thread类定义了run()方法,用于启动当前的线程
在启用当前的线程的时候,虚拟机会自动调用于run()方法
线程的其他方法:
sleep:使线程进入到睡眠的状态,需要唤醒的时间,会抛异常
setDaemon:将当前的线程设置为一个后台的线程,但主线程结束,它也会跟着结束
Join:合并一个线程,会抛出异常-->合并进来,join被谁调用,谁就执行.
currentThread:获取当前运行的线程.
package com.javami.kudy.DemoThread; public class ThreadTest { public static void main(String[]args) { int num = 0; MyThread mt = new MyThread(); //为当前的线程起名字 mt.setName("kudy-0"); mt.setDaemon(true); //设置为后台线程,但主线程执行完毕.后台线程也会跟着退出 mt.start(); //一个进程至少有一个主线程 while(true) { if(num++>10) break; try{mt.join();}catch(Exception e){e.printStackTrace();} try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();} System.out.println(Thread.currentThread().getName()+"Main()"); } } } class MyThread extends Thread { @Override public void run() { while(true) { //获取当前线程 try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();} System.out.println(Thread.currentThread().getName()+" run()"); } } }
创建线程的方式二:
实现Runnable接口
子类覆盖父类中的run()犯法
通过Thread类创建线程,并且实现了Runnable接口中的子类
对象作为参数传递给Thread类型类的构造函数
这样做有什么好处:
线程开了,并且只是处理一个对象.用到了组合模式
为什么需要用到同步代码块或者同步函数:
由于程序在执行的时候,会出现线程不安全的问题,所以我们需要用到同步函数或者同步代码块.
导致线程不安全的注意问题:
1.多个线程随机访问延时
2.线程随机性
同步代码块:
Synchronized(对象) //锁旗标 0开 1关
{
需要同步的代码;
}
同步代码可以解决安全的问题根本原因在于对象上面,该对象如同锁的功能.
同步函数:
在函数上面加上synchronized修饰即可.
Public synchronized void sale()
{
//同步代码块使用的锁是this这个锁
}
死锁:
发生在相互调用的情况下,一个同步函数里要用到和同步代码块相同的锁,同步代码块又去调用函数,就用到函数的锁,两者都会锁死,发生死锁的问题.
1 package com.javami.kudy.DemoThread; 2 3 public class TicketsSale { 4 5 /** 6 * @param args 7 * 多线程售票:同时开启4个线程售票 8 * 9 * 线程安全问题在理想的状态下,不容易出现,但一旦出现就是对软件的影响是非常之大 10 * Thread-1 -1 11 Thread-0 0 12 Thread-3 1 13 ----↓ 14 同步代码块 15 与同步函数: 16 17 */ 18 public static void main(String[] args) { 19 SaleThread st = new SaleThread(); 20 21 //4个进程产生 22 new Thread(st).start(); 23 new Thread(st).start(); 24 new Thread(st).start(); 25 new Thread(st).start(); 26 } 27 } 28 29 class SaleThread implements Runnable 30 { 31 String lock = ""; //锁期标 32 private int tickets = 10; //10张车票 33 @Override 34 public void run() 35 { 36 37 while(true) // 0 1 2 3 等待我执行完毕别的线程才去抢 38 { 39 synchronized(lock) 40 { 41 if(tickets<1) 42 break; 43 try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();} 44 System.out.println(Thread.currentThread().getName()+" "+tickets--); 45 } 46 } 47 } 48 }
同步的特点:
1.同步需要两个或者两个以上的线程.
2.多个线程使用的是同一把锁
3.为满足这两个条件,不能称其为同步.
同步的弊端:
1.当线程相当多的时候,因为每个线程都会去判断同步上的锁.这是很消耗资源的,判断该锁是否被锁上.如果锁上.需要等锁上的开了才可以互相抢进去.
无形中可以减低了程序运行的效率.
1 package com.javami.kudy.DemoThread; 2 3 public class TicketsSale1 { 4 /* 5 * 使用同步函数与同步代码块来实现4个线程 6 */ 7 public static void main(String[]args) 8 { 9 SaleThread1 st = new SaleThread1(); 10 new Thread(st).start(); 11 new Thread(st).start(); 12 st.lock = "ok"; 13 try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();} 14 new Thread(st).start(); 15 new Thread(st).start(); 16 } 17 } 18 19 class SaleThread1 implements Runnable 20 { 21 String lock = " "; 22 private int num = 100; 23 public void run() 24 { 25 if(lock.equals("ok")) 26 { 27 while(true) 28 { 29 sale(); 30 } 31 }else 32 { 33 while(true) 34 { 35 synchronized (this) { 36 if(num<1) 37 return; 38 System.out.println(Thread.currentThread().getName()+"else"+num--); 39 } 40 } 41 } 42 } 43 44 45 private synchronized void sale() { 46 if(num<1) 47 return; 48 System.out.println(Thread.currentThread().getName()+"if"+num--); 49 } 50 }
线程间的通信:
两个线程之间可以相互的通信
一个线程可以通过wait方法等待,这时CPU会让给别程
通过进行等待的线程需要其他线程调用notify()方法唤醒.
1 package com.javami.kudy.DemoThread; 2 3 /* 4 * 通信必须要在同步函数里面去解决的 5 */ 6 class SQL 7 { 8 public String name; 9 public String sex; 10 public boolean b = false; 11 } 12 class DBA implements Runnable 13 { 14 SQL sql ; 15 public DBA(SQL sql) 16 { 17 this.sql = sql; 18 } 19 public void run() 20 { 21 int num = 0; 22 while(true) 23 { 24 synchronized (sql) { 25 if(sql.b) //为真.我等待,让给别的进程~ 26 try{sql.wait();}catch (Exception e) {e.printStackTrace();} 27 if(num ==0) 28 { 29 sql.name = "小细"; 30 sql.sex = "男"; 31 }else 32 { 33 sql.name = "美怡"; 34 sql.sex = "女"; 35 } 36 num = (num+1)%2; //实现了来回打印 37 sql.b = true; 38 sql.notify(); //等待完毕需要把你唤醒~ 39 } 40 } 41 } 42 } 43 44 /* 45 * 编码员 46 */ 47 class Coder implements Runnable 48 { 49 SQL sql ; 50 public Coder(SQL sql) 51 { 52 this.sql = sql; 53 } 54 public void run() 55 { 56 while(true) 57 { 58 synchronized (sql) { 59 if(!sql.b) //如果为假-->我等待 60 try{sql.wait();}catch(Exception e){e.printStackTrace();} 61 System.out.print(sql.name+" "); 62 System.out.println(sql.sex+" "); 63 sql.b = false; //标记为假~~并且把这个进程唤醒 64 sql.notify(); 65 } 66 } 67 } 68 } 69 public class SqlThread { 70 71 /** 72 * 模仿数据库的操作,实现边读取,边打印 73 */ 74 public static void main(String[] args) { 75 SQL sql = new SQL(); 76 DBA dba = new DBA(sql); 77 Coder cr = new Coder(sql); 78 new Thread(dba).start(); //一个线程交换位置 79 new Thread(cr).start(); //另外一个线程马上就打印出来 80 } 81 82 }
思考:
wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
wait(),sleep()有什么区别?
Wait():假设我同步代码块抢到啦.我执行wait()方法,就会让该线程等待.让别的线程执行.
Notiyf():唤醒等待中的线程-->下一个
notiyfyAll(): 唤醒在此对象监视器上等待的所有线程。
Sleep():这个是去睡觉去啦~~~不需要你唤醒的,这家伙是自然醒.
使用1.5的lock和condition解决存和取之间的通信问题
1 package com.javami.kudy.DemoThread; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurrent.locks.Lock; 5 import java.util.concurrent.locks.ReentrantLock; 6 7 /* 8 * 锁必须要一样,才能称得上同步 9 */ 10 class MyArray1 11 { 12 private int[] arr = new int[10]; 13 private int savePos = 0; 14 private int getPos = 0; 15 private Lock lock = new ReentrantLock(); //创建一个锁 16 private int count = 0; 17 private Condition isFull = lock.newCondition(); //返回一个通信对象 18 private Condition isEmpty = lock.newCondition();//返回的地址一样 19 20 public void add(int num) throws InterruptedException 21 { 22 //但我执行慢的时候.我这个锁会开.开的时候.一看count==10等待..又抢,如果是你.你就等待.否则就是get 但是执行完毕一定要把当前的线程唤醒 23 try 24 { 25 lock.lock();//开锁 26 while(count==10) 27 isFull.await();//if等于10了,你就必须等待 28 if(savePos==10) 29 savePos = 0; 30 arr[savePos++] = num; 31 count++; 32 }finally 33 { 34 isEmpty.signal();//唤醒 35 lock.unlock(); 36 } 37 } 38 39 public int get() throws InterruptedException 40 { 41 try 42 { 43 lock.lock(); //开锁 44 while(count==0) 45 isEmpty.await();//等待 46 if(getPos==10) 47 getPos = 0; 48 count--; //不-- ~? 数组问题 49 return arr[getPos++]; 50 }finally 51 { 52 isFull.signal();//等待的哥们.我去叫醒你啦~~ 53 lock.unlock();//开锁 54 } 55 } 56 } 57 public class ArrayThreadDemo { 58 static int num = 1; 59 public static void main(String[]args) 60 { 61 final MyArray1 ma = new MyArray1(); 62 new Thread(new Runnable(){ 63 @Override 64 public void run() { 65 for(int i=0; i<30; i++) 66 { 67 try { 68 ma.add(num++); 69 } catch (InterruptedException e) { 70 // TODO Auto-generated catch block 71 e.printStackTrace(); 72 } 73 } 74 } 75 76 }).start(); 77 new Thread(new Runnable(){ 78 79 @Override 80 public void run() { 81 for(int i=0; i<30; i++) 82 { 83 try { 84 System.out.println(ma.get()); 85 } catch (InterruptedException e) { 86 // TODO Auto-generated catch block 87 e.printStackTrace(); 88 } 89 } 90 } 91 92 }).start(); 93 new Thread(new Runnable(){ 94 @Override 95 public void run() { 96 for(int i=0; i<30; i++) 97 { 98 try { 99 ma.add(num++); 100 } catch (InterruptedException e) { 101 // TODO Auto-generated catch block 102 e.printStackTrace(); 103 } 104 } 105 } 106 107 }).start(); 108 new Thread(new Runnable(){ 109 110 @Override 111 public void run() { 112 for(int i=0; i<30; i++) 113 { 114 try { 115 System.out.println(ma.get()); 116 } catch (InterruptedException e) { 117 // TODO Auto-generated catch block 118 e.printStackTrace(); 119 } 120 } 121 } 122 123 }).start(); 124 125 } 126 }
今天的知识点回顾:
总体来说:但是今天的精神很好..最后面的一道题目要多加练习
第二: 同步函数是:必须执行完才执行到你,但是同步完毕后不保证以后线程(抢)线程的情况.所以我们又用到 wait() 等待 唤醒(下一个的概念)
但是前提是同步函数里面使用.并且是同一把锁.注意锁的概念.程序要多敲-->后面理解毕竟不是很好.