1,java线程的创建
java线程
进程: 进程就是计算机的运行线索
线程: 进程的运行线索,计算机运行的最小单位
java中如何创建一个线程
1,1 以实现了Runable 接口的类的实例作为创建
Thread类的对象的构造函数的参数。
第一种创建线程方式
package corelesson5; import corelesson4.A; public class ThreadDemo1 { public static void main(String[] args) { //创建线程,就要以实现runable 接口的类的对象 作为创建 Thread 对象的构造函数的参数 A a =new A(); Thread t1 = new Thread(a);// 以对象作为参数,现在线程对象创建出来了 t1.start();// 启动线程 // r如果直接调用run 方法 就不是线程做的事了 必须用线程对象去调用 // 主函数本身是一个线程 我们称为主线程 for(;;){ System.out.println("world"); /** * 观察结果来看 两个线程都在运行 * t1 和main 主线程 其实在某个时间段切换运行。 * cpu 在单位时间点时刻,只运行一个线程, 肉眼感觉很多线程在运行 * cpu 来说已经很长时间了 */ } } } class A implements Runnable{ /** * 实现runable 接口 重写run 方法 * run 方法 就是线程要做的事情 */ @Override public void run() {// 就是线程要执行的任务 for(;;){ System.out.println("hello"); } } }
1,2创建线程方式2
直接创建类继承Thread 重写run 方法
package corelesson5; public class ThreadDemo2 { public static void main(String[] args) { // 方式2 这里创建了MyThread 的对象 就是创建了线程对象 MyThread t1 = new MyThread(); t1.start();// 启动线程 for(;;){ System.out.println("world"); } } } class MyThread extends Thread{ @Override public void run() { super.run(); for (;;){ System.out.println("hello"); } } }
两种方式都可以 建议使用第一种方式
因为 一个Runable 的实例我们就可以认为是一个任务
1.3 直接使用匿名类方式创建线程
package corelesson5; public class ThreadDemo3 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (;;){ System.out.println("hello"); } } }).start(); //内部类 new Thread(){ public void run(){ for(;;){ System.out.println("world"); } } }.start(); } }
知识点1, 多线程的程序经常每次运行结果可能不一样
Runnable(Running/Ready) 跟这个状态有关系
线程顺序,
1,先来后到
2,优先级优先(拿到锁之后 优先级减一)
3,时间片轮换
2,java线程的生命周期
newBron新生状态
Pause 阻塞状态
Runable 可执行状态(Running ,Ready两种状态)
Dead (不是stop--------->死是不能复生的 )
3,java线程中常用方法
package corelesson5; public class ThreadDemo4 { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("hello"); } }); System.out.println(t1.getName());// 获取线程的名字 t1.setName("线程1"); // 修改线程的名字 System.out.println(t1.getName()); // 获取当前线程的名字 System.out.println(Thread.currentThread().getName()); // 获取线程优先级的等级,如果线程没有设置优先级,默认是5级 System.out.println(t1.getPriority()); // 优先级的范围 , 最大的是10 ,最小的是1,一般的是5, System.out.println(Thread.MAX_PRIORITY); System.out.println(Thread.MIN_PRIORITY); System.out.println(Thread.NORM_PRIORITY); } }
3.2 重要的一些方法
Thread.sleep(),让线程睡,线程进入阻塞状态,不会参与竞争
package corelesson5; public class ThreadDemo5 { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("线程中断"); } System.out.println("helloworld"); } }); t1.start(); for (int i = 0; i <10 ; i++) { try { t1.sleep(10000);// 主线程sleep 10秒、、相当于Thread.sleep(10000) // 那个线程执行到sleep 方法 ,那个线程就去sleep } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } t1.interrupt();// 让睡眠中断 执行完以上任务之后 就唤醒线程 } }
interrupt 方法中断线程的睡眠,相当于唤醒
join() 方法 一个线程等待另一个线程运行结束
package corelesson5; public class ThreadDemo7 { public static void main(String[] args) { MyThread2 thread2=new MyThread2(); thread2.start(); try { thread2.join();//主线程等待thread2 线程运行结束之后才运行 } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i <10 ; i++) { System.out.println("hello"); } } } class MyThread2 extends Thread{ @Override public void run() { for (int i = 0; i <=10 ; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } } }
Thread.yield() 主动放弃CPU把机会放给别的线程,然后参与竞争 Running-----ready
4,ThreadLocal 类的使用
5,java线程互斥机制
---多线程之间共享数据问题
5.1 同步块
保证每一段代码同时只能被一个线程访问
关键字
synchronized(对象){}
java中每一个对象都是一把锁有且只有一把钥匙
出同步块就会释放锁的钥匙。
package corelesson5; public class ThreadDemo9 { public static void main(String[] args) { Date date =new Date(); //数据共享的问题 Thread t1= new Thread(date); Thread t2= new Thread(date); t1.start(); t2.start(); } } class Date implements Runnable{ private int i; @Override public void run() { int h; for (int j = 0; j <10 ; j++) { synchronized (this){ h=i+1; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i=h; } System.out.println(i); } } }
package corelesson5; public class ThreadDemo10 { public static void main(String[] args) { Date date =new Date(); //数据共享的问题 Thread t1= new Thread(date); Thread t2= new Thread(date); t1.start(); t2.start(); } } class Date1 implements Runnable{ private int i; @Override public synchronized void run() { int h; for (int j = 0; j <10 ; j++) { h=i+1; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i=h; System.out.println(i); } } }
5.2 同步函数 也是有锁存在的
--普通方法的锁就是 当前对象
在任何方法 前面都可以加关键字synchronized
静态函数同步块也是有锁的---》 锁是当前类的类类型
注意:多个线程共享数据要保证是同一把锁。
注意:死锁的问题 死锁程序就卡死,无法调试----》可以通过好的工具来检查死锁的问题。
两个线程相互等待对方释放锁。
要慎用synchronized 关键字。
注意:之前我们讲 stringbufferder 和stringbuilder
还有集合ArrayList / Vecter(有关键字synchronized)
5.3 synchronized很繁琐 比较不要理解而且不面向对象
java5 版本做了很好的改进 有了Lock 对象。
大多数情况下用语句。
Lock l=。。。
l.lock().
try{
//程序。。。。。
}finally{
l.unlock().
}
Lock 对象
package corelesson5; import com.sun.org.apache.xpath.internal.operations.Variable; import javax.lang.model.element.VariableElement; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreadDemo12 { public static void main(String[] args) { final Output1 output = new Output1(); new Thread(new Runnable() { @Override public void run() { while (true){ output.print("hello"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while (true){ output.print("world"); } } }).start(); } } class Output1{ //穿建了锁对象 Lock lock= new ReentrantLock(); public void print(String name){ lock.lock(); try { for (int i = 0; i <name.length() ; i++) { System.out.print(name.charAt(i)); } System.out.println(); }finally { lock.unlock(); } } }
扩展内容 线程并发下的缓存
package corelesson5; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { /** * 修改数据的时候 只能一个线程 但是如果只是读数据所有线程可以访问、 * 读写锁的机制解决这个问题。 */ HashMap<String,String >hm=new HashMap<String ,String>(); private ReentrantReadWriteLock rrw=new ReentrantReadWriteLock(); private java.lang.String value=null; public java.lang.String fetDate(String key){ /* value=hm.get(key); if(value==null){ value="hello";//将来通过其他资源获取 ,每一次获取value的时候直接从map 中获取 hm.put(key,value) ; } return value; */ rrw.readLock().lock();//进来之后先读数据 try { value=hm.get(key);// 大家都可以来读这个数据 if(value==null){ // 要进行写操作 rrw.readLock().unlock(); rrw.writeLock().lock(); try{if(value==null) { value = "hello"; hm.put(key, value); } }finally { rrw.writeLock().unlock(); rrw.readLock().lock(); } } }finally { rrw.readLock().unlock(); } return value; } }
6,java线程通讯机制
6.1通过管道流的方式进行通讯
package corelesson5; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class ThreadDemo13 { public static void main(String[] args) { PipedOutputStream out=null; PipedInputStream in=null; try { in=new PipedInputStream(); out=new PipedOutputStream(in); } catch (Exception e) { e.printStackTrace(); } Sender sender =new Sender(out); Thread t1= new Thread(sender); Receiver receiver = new Receiver(in); Thread t2= new Thread(receiver); t1.start(); t2.start(); } } class Sender implements Runnable{ private OutputStream out; public Sender(OutputStream out) { this.out = out; } @Override public void run() { for (int i = 0; i <5 ; i++) { byte value =(byte)(Math.random()*100); System.out.println("send the value is :"+value); try { out.write(value); } catch (IOException e) { e.printStackTrace(); } } } } class Receiver implements Runnable{ private InputStream in; public Receiver(InputStream in) { this.in = in; } @Override public void run() { for (int i = 0; i < 5; i++) { try { byte value=(byte)in.read(); System.out.println("receiver the value is "+value); } catch (IOException e) { e.printStackTrace(); } } } }
通过 管道进行简单的通讯,(性能较差)
6.2 通过 Thread.yield() 方式
yield(一直放弃cpu资源等待)
模拟生产者 和消费者的模型
package corelesson5; import java.util.Random; public class ThreadDemo14 { public static void main(String[] args) { FlagSend flagSend = new FlagSend(); FlagRec flagRec = new FlagRec(flagSend); Thread t1=new Thread(flagSend); Thread t2=new Thread(flagRec); t1.start(); t2.start(); } } class FlagSend implements Runnable{ int theValue; boolean flag; @Override public void run() { for (int i = 0; i <5 ; i++) { while (flag){ Thread.yield(); } theValue= new Random().nextInt(1000);// 制造食物 System.out.println("send the value is :"+ theValue); // 自己去等待 让食客去吃 flag=true; } } } class FlagRec implements Runnable{ private FlagSend flagSend; public FlagRec(FlagSend flagSend) { this.flagSend = flagSend; } @Override public void run() { for (int i = 0; i <5 ; i++) { while (!flagSend.flag){ Thread.yield(); } System.out.println("receiver the value is :"+flagSend.theValue); flagSend.flag=false; } } }
效率也比较差 。yield 是放弃了cup 然后又去死循环的竞争 放弃,给CPU 会造成很大的压力。
6.3线程间通讯 wait/notify 方式来通讯 ,等待 和唤醒其他线程。
概念: 任何一个对象 都拥有一个线程等待池,挂在同一个对象的线程等待池中的线程之间可以互相唤醒。
所以 wait/notify 方法是属于object 类的。
wait 方法的使用必须放在synchronized 同步块中。
package corelesson5; import java.util.Random; public class ThreadDemo15 { public static void main(String[] args) { WaitSeed send = new WaitSeed(); waitRec rec = new waitRec(send); Thread t1 = new Thread(send); Thread t2 = new Thread(rec); // 把t2 线程设置为守护线程,当只有守护线程运行的时候程序自动结束 t2.setDaemon(true); t1.start(); t2.start(); } } class WaitSeed implements Runnable{ boolean flag; int theValue; @Override public void run() { for (int i = 0; i < 5; i++) { synchronized (this){ while (flag){ // 为什么 需要用while 存在中断和虚假唤醒 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 生产者生产食物 theValue= new Random().nextInt(1000); System.out.println("send the value is:"+theValue); // 自己去等待 flag= true; //唤醒消费者 this.notify(); } } } } class waitRec implements Runnable{ // 把生产者作为你的成员 private WaitSeed send; public waitRec(WaitSeed send) { this.send = send; } @Override public void run() { // 不知道生产者生产多少食物,生产多少消费多少 while(true) { synchronized (this){ while (!send.flag){ try { send.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 不等待的话 就消费食物 System.out.println( "receiver the value is :"+send.theValue); send.flag=false; send.notify(); } } } }
面试题:
先A线程运行10次, 然后B线程运行20次
如此反复50次
理解:A线程生产者, 生产食物需要循环10次,
B线程是消费者 消费食物循环20 次,
package corelesson5; public class ThreadDemo18 { public static void main(String[] args) { final Business1 bus = new Business1(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { bus.a(); } } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 50; i++) { bus.b(); } } }).start(); } } class Business1{ boolean flag; public void a(){ synchronized (this){ while(flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < 10; i++) { System.out.println("A线程循环"+i); } flag=true; this.notify(); } } public void b(){ // B是消费者 synchronized (this){ while(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //循环20次 for (int i = 0; i < 20; i++) { System.out.println("B 线程循环"+i); } flag=false; this.notify(); } } }
6.4 java 中引入了新的线程通讯方式(了解)
用的是锁机制
7,线程范围内数据共享
模拟一个缓存map ,每次都从map 中取,没有就往里面放数据。
HashMap<Thread,Object> key,就是当前线程的对象 value 就是你要的数据,不管经过那个模块 就可以先去map 中取。
Thread.currentThread() 获取当前线程对象
package corelesson5; import java.util.HashMap; import java.util.Random; public class ThreadDemo20 { private static HashMap<Thread,Integer> data = new HashMap<>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int value= new Random().nextInt(10000); data.put(Thread.currentThread(),value); A a = new A(); a.getDate(); B b = new B(); b.getDate(); C c = new C(); c.getDate(); } }).start(); } } static class A{ public void getDate(){ Thread t =Thread.currentThread(); int value =data.get(t); System.out.println(t.getName()+" from A get data is:"+value); } } static class B { public void getDate(){ Thread t =Thread.currentThread(); int value =data.get(t); System.out.println(t.getName()+" from B get data is:"+value); } } static class C{ public void getDate(){ Thread t =Thread.currentThread(); int value =data.get(t); System.out.println(t.getName()+" from C get data is:"+value); } } }
java 中 提供了ThreadLocal 这个类,已经完成了类似的功能,可以直接使用。
package corelesson5; import java.util.HashMap; import java.util.Random; public class ThreadDemo20 { private static HashMap<Thread,Integer> data = new HashMap<>(); private static ThreadLocal<Integer> tl=new ThreadLocal<>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int value= new Random().nextInt(10000); data.put(Thread.currentThread(),value); tl.set(value); System.out.println("put to "+Thread.currentThread().getName() +"is:"+ value +" set tl value is:"+value); A a = new A(); a.getDate(); B b = new B(); b.getDate(); C c = new C(); c.getDate(); } }).start(); } } static class A{ public void getDate(){ Thread t =Thread.currentThread(); int value =data.get(t); System.out.println(t.getName()+" from A get data is:"+value +".........threadlocal is:"+tl.get()); } } static class B { public void getDate(){ Thread t =Thread.currentThread(); int value =data.get(t); System.out.println(t.getName()+" from B get data is:"+value+".........threadlocal is:"+tl.get()); } } static class C{ public void getDate(){ Thread t =Thread.currentThread(); int value =data.get(t); System.out.println(t.getName()+" from C get data is:"+value+".........threadlocal is:"+tl.get()); } } }
主要 用两个方法 tl.set() 放数据 tl.get() 取数据 tl.remove(); 删除。
7.3 写一个类,使得该类砸创建对象时,创建完成之后直接就是同一个线程,同一个对象,不同线程,对象不同。
package corelesson5; class ThreadDemo21{ public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { UserService us=UserService.getInstance(); A a = new A(); B b = new B(); a.print(); b.print(); } }).start(); } } static class A{ public void print(){ System.out.println("from A==="+Thread.currentThread().getName()); UserService us =UserService.getInstance(); System.out.println("from A==="+Thread.currentThread().getName()+"..A..."+us); } } static class B{ public void print(){ System.out.println("from B==="+Thread.currentThread().getName()); UserService us =UserService.getInstance(); System.out.println("from B==="+Thread.currentThread().getName()+"..B..."+us); } } } public class UserService { private static ThreadLocal<UserService> tl=new ThreadLocal<>(); private UserService(){}// 线程私有 ,不然别人一new 就是一个对象 public static UserService getInstance(){ UserService us=tl.get(); if(us==null){ us= new UserService(); tl.set(us); } return us; }// 构造函数私有的时候 我们就需要通过静态的方法来返回类的对象。 }
不管是单例模式 还是这种情况,我们都是的构造函数peivate
package corelesson5; import java.lang.reflect.Constructor; class ThreadDemo21{ public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { UserService us=UserService.getInstance(); A a = new A(); B b = new B(); a.print(); b.print(); } }).start(); } /* 通过反射创建对象,反射可以使得他无效*/ System.out.println("==================================="); Class c= UserService.class; try { Constructor<UserService>cs=c.getDeclaredConstructor(new Class[]{});// 获得自己声明的构造函数 cs.setAccessible(true);//private 设置访问方式 UserService us1=cs.newInstance(new Object[]{}); UserService us2=cs.newInstance(new Object[]{}); System.out.println(us1=us2);// 看看是不是同一个 } catch (Exception e) { e.printStackTrace(); } } static class A{ public void print(){ System.out.println("from A==="+Thread.currentThread().getName()); UserService us =UserService.getInstance(); System.out.println("from A==="+Thread.currentThread().getName()+"..A..."+us); } } static class B{ public void print(){ System.out.println("from B==="+Thread.currentThread().getName()); UserService us =UserService.getInstance(); System.out.println("from B==="+Thread.currentThread().getName()+"..B..."+us); } } } public class UserService { private static ThreadLocal<UserService> tl=new ThreadLocal<>(); private UserService(){}// 线程私有 ,不然别人一new 就是一个对象 public static UserService getInstance(){ UserService us=tl.get(); if(us==null){ us= new UserService(); tl.set(us); } return us; }// 构造函数私有的时候 我们就需要通过静态的方法来返回类的对象。 }
8,java线程池
线程并不是越多越好,如果无限制的穿建线程, 那么线程的创建和销毁都有很大的消耗,
希望 不管执行多少任务,都用固定的线程数来执行
package corelesson5; public class ThreadDemo22 { public static void main(String[] args) { /** 创建了10 个线程 执行了10 个任务 * 不希望有无休止的线程 * 不管有多少个任务,都让固定的线程来执行 * 可以让一个线程执行多个任务。 */ for (int i = 1; i <=10; i++) { final int task=i; new Thread(new Runnable() { @Override public void run() { System.out.println("任务"+task); } }).start(); } } }
线程池也是java5 之后引入的
如何创建线程池?
Executors 类
newFixedThreadPool() 穿建固定线程数的线程池
newSingleThreadExecutor();// 创建了单一线程,线程只有一个线程。
newCachedThreadPool();//默认创建线程,这些线程会被缓存起来,自动判断要不要创建新的线程
package corelesson5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadDemo23 { public static void main(String[] args) { // 创建一个线程池,下面是3个线程并发处理了10 个任务 ExecutorService threadPool= Executors.newFixedThreadPool(3);// 创建3个固定的线程 for (int i = 0; i < 10; i++) {// 创建了10个runnable 的实例 都是用同一个线程池的对象threadPool 来执行的 final int task=i; threadPool.execute(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { // 每个任务循环10次。 System.out.println(Thread.currentThread().getName()+"执行第"+task+"个任务的第:"+j+"次循环"); } } }); } threadPool.shutdown(); // 关闭线程池 } }
穿建线程池方式二
newCachedThreadPool();//默认创建线程,这些线程会被缓存起来,自动判断要不要创建新的线程
newSingleThreadExecutor();// 创建了单一线程,线程只有一个线程。
newFixedThreadPool() 穿建固定线程数的线程池
newScheduledThreadPool(1)
package corelesson5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadDemo23 { public static void main(String[] args) { // 创建一个线程池,下面是3个线程并发处理了10 个任务 // ExecutorService threadPool= Executors.newFixedThreadPool(3);// 创建3个固定的线程 // ExecutorService threadPool=Executors.newSingleThreadExecutor();// 创建了单一线程,线程只有一个线程。 ExecutorService threadPool=Executors.newCachedThreadPool();//默认创建线程,这些线程会被缓存起来,自动判断要不要创建新的线程 for (int i = 0; i < 10; i++) {// 创建了10个runnable 的实例 都是用同一个线程池的对象threadPool 来执行的 final int task=i; threadPool.execute(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { // 每个任务循环10次。 System.out.println(Thread.currentThread().getName()+"执行第"+task+"个任务的第:"+j+"次循环"); } } }); } threadPool.shutdown(); // 关闭线程池 } }
package corelesson5; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadDemo24 { public static void main(String[] args) { ScheduledExecutorService threadPool= Executors.newScheduledThreadPool(1); /** 4 个参数 * 1,执行什么任务 * 2 首次执行的延迟时间, * 3 连续执行的周期 * 4, 参数的时间单位 * */ threadPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(" 爆炸了。。。。"); } },0,2, TimeUnit.SECONDS); } }
8,Callable
Callable 有返回值,
package corelesson5; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ThreadDemo25 { public static void main(String[] args) { /**前面执行任务都是run 方法执行任务,而run 方法是没有返回值的。 * 在多线程并发的时候 我们不知道线程什么时候结束 * 如果线程执行完了能够返回一个值,那么我们就知道线程运行结束了。 * */ ExecutorService threadPool= Executors.newSingleThreadExecutor(); //Callable<T> 泛型的具体类型就是返回值的类型 也决定了 Future 泛型的类型 Future<Integer> future = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("线程任务开始。。。。"); Thread.sleep(3000); System.out.println("线程任务结束。。。。"); return 10; } }); try { int value= future.get();// 取返回值 System.out.println(value); } catch (Exception e) { e.printStackTrace(); } } }
关于线程基础内容 就先了解这么多。