静态内部类?局部内部类?
以及用接口创建对象是什么情况,为什么不这样就会矛盾?
这篇中的第一个例子的细节要在挖一下。
引入的基本理念:
函数式接口的定义
即lamda表达式就是针对函数式接口来进行实现的。
具体代码示例
package Thread; public class Demo05 { //静态内部类 static class Love2 implements Ilove { @Override public void love(int a) { System.out.println("我喜欢"+a); } } public static void main(String[] args) { Ilove ll =new Love(); ll.love(10); ll = new Love2(); ll.love(20); //局部内部类 class Love3 implements Ilove { @Override public void love(int a) { System.out.println("我喜欢"+a); } } ll = new Love3(); ll.love(30); ll = new Ilove() { @Override public void love(int a) { System.out.println("我喜欢"+a); } }; ll.love(40); ///lamda表达式 ll = (a) ->{ System.out.println("我喜欢"+a); }; ll.love(50); } } //只定义了一个方法的函数式接口 interface Ilove { void love(int a); } //1.通过外部定义类,在main方法中创建对象调用 class Love implements Ilove { @Override public void love(int a) { System.out.println("我喜欢"+a); } }
这里发现还是对上面写到的各种创建类不熟悉,后面补习过后还是要记录记忆一下。
ll = a -> System.out.println("我喜欢"+a);
其中lamda表达式可以简化成这种形式,注意有多行时候对应加花括号和大括号就可以了。
其中箭头后的部分就是重写的内容,前面的就是形参。ll即是类创建的对象。
对应的还有个观测线程状态的方法,有具体实际需求时再去看使用细节吧,其实也就是调用返回一个值运行的过程。
大概的分类和运行如下图所示,后面会具体对各个状态进行呈现。相对应的对于线程各种状态设置的方法:
基本原则是不推荐手动停止,最后设置一个标志位,当其运行到指定标志时自己停止。
package Thread;//线程停止测试 public class Demo06 implements Runnable{ private boolean flag = true; @Override public void run() { int i = 0; while(flag) { System.out.println("正在运行"+i++); } } //设置一个公开的方法转换标志位使线程停止 public void stop() { this.flag = false; } public static void main(String[] args) { Demo06 dd = new Demo06(); new Thread(dd).start(); for (int i = 0; i < 5000; i++) { if(i==4000) { dd.stop(); System.out.println("停止了"); } } } }
基本就在上面代码中所示了,没啥特殊的需要说了。
其实简单理解,线程sleep的方法就可以理解成java中实现延时的方法,注意以毫秒为单位即可。
在具体项目中通过引入延时可以放大问题所在,其他的就当作一个普通延时去理解即可。
其实意思就是让执行态的线程回到就绪态让CPU重新选择执行一次。
package Thread; public class Demo07 { public static void main(String[] args) { ylie yy =new ylie(); new Thread(yy,"a").start(); new Thread(yy,"b").start(); } } class ylie implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始了"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"结束了"); } }
此时即是没有礼让成功。
这里就不写了。。就是简单的在主线程执行的过程中join一个其他线程的示例。这里注意就算没有join也会并发执行,但是加了join后就会优先执行完加入的线程
同样的优先级高的不是一定先执行,只是在先执行的可能上有了更高的比重。
package Thread;//线程优先级测试 public class Demo08 { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority()); prio pp =new prio(); Thread t1 = new Thread(pp); Thread t2 = new Thread(pp); Thread t3 = new Thread(pp); Thread t4 = new Thread(pp); //先设置优先级在启动 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(10); t4.start(); } } class prio implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority()); } }
由此可见高的不是一定会先执行。
第四条举的例子也都是守护线程的例子。
设置守护线程通过setDaemon(true)来实现。
反正就是记住一个观点,虚拟机要保证用户线程执行完毕,然后就不用管守护线程,跟着程序虚拟机就自然地一起结束了。
例子不想写了,也就是一个设置看结果的呈现,有兴趣翻视频。
并发:同一个对象被多个线程同时操作。
同步的基本出发点即如下:
形成的条件:队列+锁
锁的相关概念
集合的不安全示例,也可以理解成由于太快导致多个线程添加到了集合中的同一个位置。
缺陷的具体体现即在于因为只有对资源进行修改的代码才需要锁的机制,所以当代码只是只读的类型的时候,则会造成一定的浪费。
加了同步方法的代码:
package Thread; public class Demo09 { public static void main(String[] args) { Buy bb =new Buy(); Thread t1 = new Thread(bb,"yi"); Thread t2 = new Thread(bb,"er"); Thread t3 = new Thread(bb,"san"); t1.start(); t2.start(); t3.start(); } } class Buy implements Runnable { private int ticnum = 10; boolean flag = true; @Override public void run() { while(flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //该方法变成了同步方法,锁的是定义该方法的类,即this private synchronized void buy() throws InterruptedException { if (ticnum<=0) { flag = false; return; } Thread.sleep(500); System.out.println(Thread.currentThread().getName()+"拿到了"+ticnum--); } }
结果:
此时包括结果出现的过程也可以发现是完全按顺序进行,并且也不会资源越界。
然后这里说明一下锁的对象的选择和方法的使用:
这里额外提一嘴即是JUC中提供了一些现成的安全的集合,即不用通过写同步方法的标志也可以实现线程的安全问题,后面到并发的时候再细说。
package Thread;//死锁的示例 public class Demo10 { public static void main(String[] args) { Makeup m1 = new Makeup(0,"one"); Makeup m2 = new Makeup(1,"two"); Thread t1 =new Thread(m1); Thread t2 =new Thread(m2); t1.start(); t2.start(); } } class Lip { } class mirro { } class Makeup implements Runnable { private int chio; private String name; static Lip ll = new Lip(); static mirro mm = new mirro(); public Makeup(int chio, String name) { this.chio = chio; this.name = name; } @Override public void run() { try { make(); } catch (InterruptedException e) { e.printStackTrace(); } } private void make() throws InterruptedException { if (chio == 0) { synchronized (ll) { System.out.println(this.name+"有L锁"); Thread.sleep(1000); synchronized (mm) { System.out.println(this.name+"有M锁"); } } } else { synchronized (mm) { System.out.println(this.name+"有M锁"); Thread.sleep(1000); synchronized (ll) { System.out.println(this.name+"有L锁"); } } } } }
执行后即会发现会一直卡在这个状态不继续执行下去。
修正的方法:
private void make() throws InterruptedException { if (chio == 0) { synchronized (ll) { System.out.println(this.name+"有L锁"); Thread.sleep(1000); } synchronized (mm) { System.out.println(this.name+"有M锁"); } } else { synchronized (mm) { System.out.println(this.name+"有M锁"); Thread.sleep(1000); } synchronized (ll) { System.out.println(this.name+"有L锁"); } } }
即就如上面所说,只要出了同步方法块,就相当于解除了锁,此时就不会达成互相一直持有这么一个状态了。
最后再总结一下:
理解成新版本提供的更加方便的实现方式即可。
具体的使用示例(用前面的例子):
package Thread;//lock的示例import java.util.concurrent.locks.ReentrantLock;public class Demo11 { public static void main(String[] args) { Buyy bb =new Buyy(); Thread t1 = new Thread(bb,"yi"); Thread t2 = new Thread(bb,"er"); Thread t3 = new Thread(bb,"san"); t2.start(); t1.start(); t3.start(); }}class Buyy implements Runnable{ private int ticnum = 10; boolean flag = true; private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { try{ lock.lock(); while(flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { lock.unlock(); } } private void buy() throws InterruptedException { if (ticnum<=0) { flag = false; return; } Thread.sleep(500); System.out.println(Thread.currentThread().getName()+"拿到了"+ticnum--); }}
同步方法和lock锁的区别与联系:
具体的区别简单理解就是可以指定锁的区域,即在这个区域内执行的过程是要严格加锁的。
物理模型即是通过中间资源作为媒介,生产者生产和消费者使用的这么一个过程。
因此自然引入了线程通信及其分析:
Java自己提供的用于解决线程间通信的方法:
emm这种方法知道有这么个概念就可以了,反正就是以中间的缓冲区作为媒介和判断,进而实现两个对象之间的通信。
其中表演和观看就分别对应着生产者和消费者的方法。
这里需要注意的点:
代码示例:
package Thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//线程池的测试public class Demo13 { public static void main(String[] args) { //1.创建服务,创建线程池 ExecutorService ee = Executors.newFixedThreadPool(10); //2。将线程放入池子中并执行,丢入的是实现Run的接口就可 ee.execute(new myth()); ee.execute(new myth()); ee.execute(new myth()); ee.execute(new myth()); //关闭连接 ee.shutdown(); }}class myth implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); }}