进程是一个应用程序;线程是应用程序中的执行单元。 一个进程可以由多个线程,最基本的包括主线程和垃圾回收线程。 一个线程占一个栈空间,不同栈空间相互独立,互不共享,堆和方法区内存是对这些栈是共享的。JVM内存结构 所以可以看出,main函数(主线程)结束不代表线程结束,因为还有其它分支线程进行弹栈压栈操作。
1.创建一个Thread类的子类,并重写run()方法 2.实现Runnable接口,重写run()方法,并用new Thread(实现接口的对象)进行封装 3.使用匿名类的形式快速实现Runnable接口,并进行封装
五种状态:新建状态——就绪状态——运行状态——阻塞状态——死亡状态。 new Thread()标志着为新建状态; start()标志着进入就绪状态,又叫做可执行状态,拥有抢夺CPU时间片的权利(执行权),当抢到了一块CPU时间片之后,就开始执行run方法; run()标志着进入运行状态,当这个时间片用完之后会再次回到就绪状态,然后重新抢夺时间片并继续run方法的运行(run的内容不会再次从头开始运行); 遇到阻塞事件(用户输入或者sleep)标志着进入阻塞状态,放弃当前的时间片,回到就绪状态重新夺取时间片; run()的内容运行完之后就进行死亡状态。
sleep()函数是静态方法,作用是对当前线程(所处的代码块决定)进行休眠。interrupt()函数是非静态方法,可以中断线程中的休眠时间这种方式是通过Java的异常处理机制来实现的。 真正意义上的线程终止是通过添加一个布尔标记来终止线程的。
...... rm.run=false;//终止t4线程 ...... class MyRunnable implements Runnable{ boolean run=true;//布尔标记 @Override public void run() { // TODO Auto-generated method stub if (run) { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } }else { //save return; } } }真正的多线程并发是指同一时间点有多个线程同时运行。对于单核CPU一次只能运行一个线程,系统会对多个线程进行调度,在线程之间来回切换,在宏观上看来像是在同时运行。
并发: 当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。 并行: 当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
package birda; //创建线程的三种方法 public class Mythread extends Thread{ @Override public void run() { for(int i=0;i<1000;i++) { System.out.println(Thread.currentThread().getName()+"--->"+i); } } public static void main(String[] args) { // TODO Auto-generated method stub //继承Thread Mythread t1=new Mythread(); t1.setName("t1线程"); t1.start(); for(int i=0;i<1000;i++) { System.out.println("主线程--->"+i); } try { Thread.sleep(1000*5);//当前线程睡眠;此时当前线程为主线程 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("开始执行t2线程的内容"); //实现Runnable接口 Thread t2=new Thread(new MyRunnable()); t2.setName("t2线程"); t2.start(); try { Thread.sleep(1000*5);//当前线程睡眠;此时当前线程为主线程 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("开始执行t3线程的内容"); //匿名类的形式创建线程 Runnable r=new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } try { Thread.sleep(1000 * 60); } catch (InterruptedException e) { // TODO Auto-generated catch block } System.out.println("已经被睡眠60s了"); if (Thread.interrupted()) { System.out.println("线程被中断过"); } } }; Thread t3=new Thread(r); t3.setName("线程t3"); System.out.println("线程t3的名字为: "+t3.getName()); t3.start(); //中断线程睡眠 t3.interrupt(); //终止线程 //通过布尔标记的方式终止 MyRunnable rm=new MyRunnable(); Thread t4=new Thread(rm); t3.setName("线程t4"); t3.start(); //主线程睡眠5秒 try { Thread.sleep(100*5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } rm.run=false;//终止t4线程 } } class MyRunnable implements Runnable{ boolean run=true; @Override public void run() { // TODO Auto-generated method stub if (run) { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } }else { //save return; } } }1.线程调度模型 抢占式调度模型:优先级高的线程抢夺的时间片大概率是多的(并非所有时候都这样)【Javja采用的调度机制】 均分式调度模型:每一个线程所占用的时间片的多少是相等的。
2.Java中的调度方法 实例方法: getPriority()和setPriority()分别为得到线程优先级和设置优先级 join()是让当前线程进入阻塞状态,先运行调度该方法的线程
静态放法 yield()是让当前线程从运行状态进入就绪状态
package birda; public class Mythread extends Thread{ @Override public void run() { // Thread.currentThread().yield(); for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"--->"+i); } } @SuppressWarnings("static-access") public static void main(String[] args) { // TODO Auto-generated method stub //继承Thread Thread t1=new Mythread(); t1.setName("t1线程"); t1.start(); //设置优先级 for(int i=0;i<10;i++) { Thread.currentThread().yield();//线程让位一次,从运行状态进入就绪状态 System.out.println("主线程--->"+i); } try { Thread.sleep(1000*2); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(t1.getPriority()); //创建第二个线程 Runnable r=new MyRunnable(); Thread t2=new Thread(r); t2.setName("t2线程"); t2.start(); System.out.println(t2.getPriority()); } } class MyRunnable implements Runnable { @Override public void run() { Thread t3=new Mythread(); t3.setName("t3线程"); t3.start(); try { t3.join();//当前线程受阻塞,先运行t3再运行其它线程 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"--->"+i+" "); //System.out.println(Thread.currentThread().getName()); } } }线程产生安全问题的原因是什么:两个线程a,b同时访问一个数据m。b应该在a执行完成之后访问m。但是如果ab是并发的,线程a会改变m的值,存在一种可能就是线程b访问的是最开始的那个m。 存在线程安全问题的三个条件: 1.线程是并发的 2.共享数据 3.数据有修改的行为
看下面的程序代码:
public class Threadsafe2 extends Thread{ private Account account; public Threadsafe2(Account t) { this.account=t; } @Override public void run() { account.withdraw(5000); System.out.println(Thread.currentThread().getName()+":取款金额为:"+5000+" 余额:"+account.getbalance()); } public static void main(String[] args) { Account account=new Account("account-001",10000); Threadsafe2 t1=new Threadsafe2(account); t1.setName("线程t1"); t1.start(); Threadsafe2 t2=new Threadsafe2(account); t2.setName("线程t2"); t2.start(); } } //另一块代码 public class Account { private String account; private int accountmoney; public Account(String name,int money) { this.account=name; this.accountmoney=money; } //取钱 public void withdraw(int m) { int before=accountmoney; int after =before-m; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.getmoney(after); } //显示账户余额 public void getmoney(int n) { accountmoney=n; } public int getbalance() { return accountmoney; } } 线程t1:取款金额为:5000 余额:5000 线程t2:取款金额为:5000 余额:5000线程同步机制就是线程按照先后顺序执行,这样会牺牲一部分的效率; 线程异步就是多个线程直接按相互独立,执行过程互不影响,就是线程并发。
synchronized代码块的用法是占用一个对象锁,只让当前占用这个锁的线程运行,其他用到这个锁的线程排队等待。
synchronized(m){ //m是需要排队的线程的共享变量 }当m为this的时候:
//整体代码与上面相同,修改的部分如下: public void withdraw(int m) { synchronized (this) { int before = accountmoney; int after = before - m; try { Thread.sleep(100);//睡眠的目的是不及时更新余额,制造安全问题 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.getmoney(after); } } //执行结果 t1:取款金额为:5000 余额:5000 t2:取款金额为:5000 余额:0this在这里面的指的是Account account=new Account("account-001",10000); 每一个对象有一把锁,在这里面account是被t1和t2共享的,那么只能有一个线程占用这把锁,当这个所被占用后,其他线程就需要等待。
锁池lockpool:线程进入锁池找共享对象的对象锁,此时会释放之前占有的CPU时间片,接下来有两种可能,如果找到了就进入就绪状态重新抢夺时间片,如果没找到那就在锁池中继续等待。
当m为成员变量时
public void withdraw(int m) { synchronized (account) { int before = accountmoney; int after = before - m; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.getmoney(after); } } //执行结果 t1:取款金额为:5000 余额:5000 t2:取款金额为:5000 余额:0成员变量account是属于共享对象Account account=new Account("account-001",10000);的,而这个对象是共享的,那么account也是被共享的。
当m为常量时
public void withdraw(int m) { synchronized ("123") { int before = accountmoney; int after = before - m; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.getmoney(after); } } //执行结果 t1:取款金额为:5000 余额:5000 t2:取款金额为:5000 余额:0当m为字符串常量时一定会发生线程同步,并且是对全部线程都是同步的,因为字符串常量“123”只有一把锁。当m为静态成员变量原理也是一样的。
静态变量和字符串常量都是存放在方法区中的
当m为局部变量时 首先需要知道,局部变量随着函数每一次调用都是被重新创建。
public void withdraw(int m) { String a=new String(); //如果是String s=“123”的s也是局部变量,但是其内存是在方法区中的,会共享同一块内存 //所以会发生线程同步现象 synchronized (a) { int before = accountmoney; int after = before - m; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.getmoney(after); } } //执行结果 t2:取款金额为:5000 余额:5000 t1:取款金额为:5000 余额:5000Java中的三大变量 实例变量:堆中(共享) 静态变量:方法区中(共享) 局部变量:栈中(不共享),永远不会发生线程安全问题。
扩大同步
public void run() { synchronized (account) { account.withdraw(5000); System.out.println(Thread.currentThread().getName() + ":取款金额为:" + 5000 + " 余额:" + account.getbalance()); } }synchronized用在实例方法上面 此时只能锁的是this,不灵活,同步的是整个方法,无故扩大同步范围,影响执行效率,优点:代码节俭 用法:
public synchronized void withdraw(int m) { String a=new String(); int before = accountmoney; int after = before - m; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.getmoney(after); }总结 1.synchronized只是对大括号里面的代码同步执行,其余代码正常执行。 2.锁更像是一个内存,当这个内存被一个线程占用了,那么其他线程就得排队。 3.StringBuffer,Vector,Hashtable都是线程安全的;StringBuilder,ArrayList,HashMap,HashSet都是线程不安全的。
1.HashMap和HashTable都实现了Map接口,但是HashMap不是线程安全的类,HashTable是线程安全的类 2.StringBuffer是线程安全的类,适合在多线程情况下进行字符串拼接;StringBuilder不是线程安全的类,在单线程的时候,StringBuilder比StringBuffer要快 3.Vector是线程安全的,ArrayList不是线程安全的 4.可以通过Collection操作将非线程安全的类转换成线程安全的类 List<Integer> list1 = new ArrayList<>(); List<Integer> list2 = Collections.synchronizedList(list1);
当有很多个线程需要启动和结束的时候,会导致系统变慢!所以引入线程池的设计思想! 1.创建一个任务容器 2.一次性启动十个线程 3.这是个线程都处于wait状态 4.有一个“任务”被扔到线程中来,那么就有一个线程被唤醒 5.被唤醒的线程执行完这个“任务”之后,继续等待下一个“任务” 6.如果有多个“任务”,就有多个线程被唤醒 当任务被执行完毕后,线程并不会结束,继续循环已经存在的线程!
手写线程池代码:万物皆对象 1.创建一个ThreadPool的类,里面要有装线程的一个容器,容器里面的类是要继承Thread的; 2.容器里面的线程是干什么用的,用来处理接收到的任务,添加任务需要有一个add函数 3.线程在ThreadPool对象的时候就应该被启动好了,所以得有一个方法,用来启动线程 4.线程启动,代表里面run被执行,线程不会关闭,所以while(true),因为对于容器对象来说,添加任务和执行任务会产生安全问题 看代码:
package lockpool; import java.util.LinkedList; public class ThreadPool { // 线程池大小 int threadPoolSize; // 任务容器 LinkedList<Runnable> tasks = new LinkedList<Runnable>(); // 试图消费任务的线程 public ThreadPool() { threadPoolSize = 10; // 启动10个任务消费者线程 synchronized (tasks) { for (int i = 0; i < threadPoolSize; i++) { new TaskConsumeThread("任务消费者线程 " + i).start(); } } } public void add(Runnable r) { synchronized (tasks) { tasks.add(r); // 唤醒等待的任务消费者线程 tasks.notifyAll(); } } class TaskConsumeThread extends Thread { public TaskConsumeThread(String name) { super(name); } Runnable task; public void run() { System.out.println("启动: " + this.getName()); while (true) { synchronized (tasks) { while (tasks.isEmpty()) { try { tasks.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } task = tasks.removeLast(); // 允许添加任务的线程可以继续添加任务 tasks.notifyAll(); } System.out.println(this.getName() + " 获取到任务,并执行"); task.run(); } } } } public class TestA { public static void main(String[] args) { ThreadPool pool = new ThreadPool(); for (int i = 0; i < 5; i++) { Runnable task = new Runnable() { @Override public void run() { //System.out.println("执行任务"); //任务可能是打印一句话 //可能是访问文件 //可能是做排序 } }; pool.add(task); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }java自带的线程池 ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 第一个参数: 10代表线程池的默认容量 第二个参数: 15代表最大容量,当任务超过10个之后,会把容量扩充至15 第三个参数: 结合第四个参数TimeUnit.SECONDS,表示经过60秒,多出来的线程还没有接到活儿,就会回收,最后保持池子里就10个 第五个参数: new LinkedBlockingQueue() 用来放任务的集合
public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); threadPool.execute(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub System.out.println("任务1"); } }); }lock是一个接口,声明一个接口类的对象: Lock lock = new ReentrantLock();
占用锁:lock.lock(); 一旦占用不会自动释放,需要自己手动释放:lock.unlock();(放在finally中进行);
Lock lock = new ReentrantLock(); Thread t1 = new Thread() { public void run() { try { log("线程启动"); log("试图占有对象:lock"); lock.lock(); log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { log("释放对象:lock"); lock.unlock(); } log("线程结束"); } }; t1.setName("t1"); t1.start();trylock trylock执行的就是在规定的时间内去抢占锁,所以存在没有占用成功的现象,所以在释放锁的时候需要一个标记来判断!
Lock lock = new ReentrantLock(); Thread t1 = new Thread() { public void run() { boolean locked = false; try { log("线程启动"); log("试图占有对象:lock"); locked = lock.tryLock(1,TimeUnit.SECONDS); if(locked){ log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); } else{ log("经过1秒钟的努力,还没有占有对象,放弃占有"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(locked){ log("释放对象:lock"); lock.unlock(); } } log("线程结束"); } }; t1.setName("t1"); t1.start();lock对象的对象交互 通过调用lock对象的newCondition方法的返回一个Condition对象,通过调用这个对象的await,signal,signalAll方法去实现线程交互
public class Mylock { public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // TODO Auto-generated method stub Thread t1 = new Thread(new Runnable() { @Override public void run() { lock.lock(); log("t1成功抢占锁"); log("t1执行五秒钟的操作"); log("t1执行暂停,并且释放锁"); try { condition.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // condition.signal(); log("t1又开始执行"); } }); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread t2 = new Thread(new Runnable() { @Override public void run() { lock.lock(); log("t2成功抢占锁"); log("t2执行五秒钟的操作"); // condition.signal(); log("t2执行完成"); condition.signalAll(); lock.unlock();//这一句必须加上,因为signal只是唤醒,并没有释放锁 } }); t2.start(); } public static void log(String str) { System.out.println(new Date() + str); } }原子访问是指一条语句执行不可拆分,不可中断的操作!比如赋值语句·int a=5 但是i++不是原子语句,分为三个原子性操作在一起的,1.取i的值;2.i加1;3.赋值给i;所以这就有可能会产生线程安全问题! java包里有一个把一些操作封装成了原子性操作!java.util.concurrent.atomic
public static void main(String[] args) throws InterruptedException { AtomicInteger atomicI =new AtomicInteger(); int i = atomicI.decrementAndGet(); int j = atomicI.incrementAndGet(); int k = atomicI.addAndGet(3); }同步测试:分别使用基本变量的非原子性的++运算符和 原子性的AtomicInteger对象的 incrementAndGet 来进行多线程测试。
package mythread; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; public class Mylock { private static int values = 0; private static AtomicInteger atomicValue = new AtomicInteger(); public static void main(String[] args) { Thread[] ts1 = new Thread[100000]; for (int i = 0; i < 100000; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub values++; } }); t.start(); ts1[i] = t; } // 等待线程结束 for (int i = 0; i < 100000; i++) { try { ts1[i].join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(values); Thread[] ts2 = new Thread[100000]; for (int i = 0; i < 100000; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub atomicValue.incrementAndGet(); } }); t.start(); ts2[i] = t; } // 等待线程结束 for (int i = 0; i < 100000; i++) { try { ts2[i].join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(atomicValue.intValue()); } }死锁现象的产生是由于共享对象被两个线程占用没有得到释放,程序无法执行下一步,运行不会出错,会永远僵持在那里。synchronized的嵌套使用比较容易造成这种现象。
package threadsafe; public class Threadsafe1 { public static void main(String[] args) { Object o1=new Object(); Object o2=new Object(); MyThreada m1=new MyThreada(o1,o2); MyThreadb m2=new MyThreadb(o1,o2); m1.setName("m1"); m2.setName("m2"); m1.start(); m2.start(); } } class MyThreada extends Thread{ private Object o1; private Object o2; public MyThreada(Object o1,Object o2) { this.o1=o1; this.o2=o2; } @Override public void run() { synchronized(o1) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized(o2) { System.out.println(Thread.currentThread().getName()); } } } } class MyThreadb extends Thread{ private Object o1; private Object o2; public MyThreadb(Object o1,Object o2) { this.o1=o1; this.o2=o2; } @Override public void run() { synchronized(o2) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized(o1) { System.out.println(Thread.currentThread().getName()); } } } }1.尽量用局部变量代替实例变量和静态变量 2.如果必须是实例变量,考虑尽量多创建几个对象 3.用synchronized
Java语言中的线程分为两大类,一类是用户线程,一类是守护线程,例如:main线程就是用户线程,垃圾回收线程就是守护线程。 守护线程的特点:一般是死循环,随着用户线程的结束而结束;用来每隔一段时间做某一件事,这也可以设置一个定时器,并且将定时器设置为守护线程 实现守护线程 语法:线程.setDaemon(true);
public class Threadsafe3 { public static void main(String[] args) { BakThread b=new BakThread(); b.setName("备份线程"); b.setDaemon(true); b.start(); for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class BakThread extends Thread{ @Override public void run() { int i=0; while(true) { System.out.println(Thread.currentThread().getName()+"--->"+i++); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } //执行结果 main-->0 守护线程--->0 守护线程--->1 main-->1 main-->2 守护线程--->2 守护线程--->3 main-->3 守护线程--->4 main-->4 守护线程--->5 main-->5 守护线程--->6 main-->6 main-->7 守护线程--->7 main-->8 守护线程--->8 main-->9 守护线程--->9 守护线程--->10作用:间隔特定的时间去执行特定的程序 三种实现方法: 1.设置线程睡眠时间(最原始) 2.java.util.Timer是封装好的定时器,可以直接用,也很少用,因为很多框架自带定时器 3.Spring框架中SpringTask里面有定时器
实现定时器
Timmer t=new Timer(); t.schedule(TimerTask task,起始时间,间隔时间); //TimerTask implements Runnable //所以任务代码可以是Runna类 public class Threadsafe4 { public static void main(String[] args) { // TODO Auto-generated method stub Timer t=new Timer(true);//true是把定时器设置为守护线程,默认是false SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" ); try { Date firsttime=sdf.parse("2020-10-11 05:48:00"); t.schedule(new MyThreadsafe4(),firsttime,1000); //也可以用匿名类的形式 //new TimerTask(){重写run方法}; } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"--->"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class MyThreadsafe4 extends TimerTask{ public void run() { SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" ); String s=sdf.format(new Date()); System.out.println(s+"数据保存了"); } } //执行结果 main--->0 2020-10-11 15:59:59数据保存了 main--->1 2020-10-11 16:00:00数据保存了 main--->2 2020-10-11 16:00:01数据保存了 main--->3 2020-10-11 16:00:02数据保存了 main--->4 2020-10-11 16:00:03数据保存了 main--->5 2020-10-11 16:00:04数据保存了 main--->6 2020-10-11 16:00:05数据保存了 main--->7 2020-10-11 16:00:06数据保存了 main--->8 2020-10-11 16:00:07数据保存了 main--->9 2020-10-11 16:00:08数据保存了 2020-10-11 16:00:09数据保存了这种方式实现的线程得到线程完成之后的返回值。用这种方式可以拿到实现结果 语法: FutureTask task=new FutureTask(Callable接口的实现类对象); Thread t=new Thread(task);t.start();Object o=task.get()
public class Threadsafe5 { public static void main(String[] args) { @SuppressWarnings("unchecked") FutureTask task=new FutureTask(new Callable() { @Override public Object call() throws Exception { System.out.println(Thread.currentThread().getName()+"--->begain"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"--->end"); int a=5; int b=10; return a+b;//自动装箱 } }); Thread t=new Thread(task); t.setName("未来线程"); t.start(); try { Object o=task.get();//会阻塞主线程 System.out.println(o); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //执行结果 未来线程--->begain 未来线程--->end 15缺点: FutureTask.get()会导致当前线程阻塞,效率比较低。
这两个方法是Object 的方法,所以所有类都有这个方法。 Object o=new Obejct();o.wait() wait()方法让执行o上的线程进入无期限等待状态。 执行o上的线程就是上面代码块所在的线程。 o.notify()是随机唤醒一个执行o上面的线程。 notifyall()唤醒在此对象上的所有等待的线程
wait()和notify()方法是建立在线程同步的基础上的。o.wait()方法会让当前线程进入等待状态,并让当前线程释放之前占用o的对象锁;notify()方法只会通知(可以执行了),不会释放之前占用o的对象锁
这里面用trylock去实现生产者和消费者模式!
start()之后,run()之前。