JAVA线程与并发(完善中)
1.1 线程创建
1.1.1 Thread class集成Thread类 实现run方法
自定义线程类继承Thread类
public class TestThread1 extends Thread {
@Override
public void run() {
for(int i = 0;i < 200;i++){
System.out.println("输出+++++++" + i);
}
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1();
testThread1.start();
for(int i = 0;i < 200;i++){
System.out.println("输出" + i);
}
}
}
1.1.2 Runnable接口实现Runable接口 实现run方法
public class TestThread3 implements Runnable {
@Override
public void run() {
for(int i = 0;i < 200;i++){
System.out.println("输出+++++++" + i);
}
}
public static void main(String[] args) {
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象来开启线程
Thread thread = new Thread(testThread3);
thread.start();
//简写 -- 可以使得 多个testThread3 对象 被多个线程同时使用
new Thread(testThread3).start();
for(int i = 0;i < 200;i++){
System.out.println("输出" + i);
}
}
}
1.1.3 实现Callable接口
创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);
顶级接口Executor(执行线程的工具) ExecutorService(真正的线程池接口)
这里的创建的线程池的不同
-
CachedThreadPool 创建带有缓存的线程池,没有核心线程,非核心线程数量不限制;调用execute将重用一起构造的线程(如果线程可用),如果没有现有线程 则重新创建。终止并重缓存中移除60秒未被使用的线程
-
ScheduledThreadPool 创建带有核心线程的线程池,拥有非核心线程,非核心线程数量不限制
-
SingleThreadPool 单一线程池 只返回一个线程,可以在发生异常或死亡时重新生成一个新的线程代替继续执行
-
FixedThreadPool 创建 可重用的固定线程数量的线程池,以共享无界队列的方式运行这些线程 ; 只有核心线程 的线程池
//缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中,在此之中的线程 超过TIMEOUT不活动,会自动被终止。
ExecutorService service = Executors.newCachedThreadPool()
//任意时间点,最多只能有固定数目的活动线程存在 此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
ExecutorService service = Executors.newFixedThreadPool(int)
//调度型线程池 这个池子里的线程可以按schedule依次delay执行,或周期执行
ExecutorService service = Executors.newScheduledThreadPool(int)
//单例线程,任意时间池中只能有一个线程
ExecutorService service = Executors.SingleThreadExecutor()
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//创建线程的方式:实现Callable接口
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WDownloader webDownloader = new WDownloader();
webDownloader.downloader(url,name);
System.out.println("下载文件名:" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://tpc.googlesyndication.com/simgad/9451486580539342396/downsize_200k_v1?w=600&h=314","1");
TestCallable t2 = new TestCallable("https://tpc.googlesyndication.com/simgad/9451486580539342396/downsize_200k_v1?w=600&h=314","2");
TestCallable t3 = new TestCallable("https://tpc.googlesyndication.com/simgad/9451486580539342396/downsize_200k_v1?w=600&h=314","3");
//创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);
//提交执行任务
Future<Boolean> future1 = service.submit(t1);
Future<Boolean> future2 = service.submit(t2);
Future<Boolean> future3 = service.submit(t3);
//获取结果
boolean res1 = future1.get();
boolean res2 = future2.get();
boolean res3 = future3.get();
//关闭服务
service.shutdown();
System.out.println(res1);
System.out.println(res2);
System.out.println(res3);
}
}
class WDownloader{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO Exception !!! ");
}
}
}
1.2 线程同步的方法
1.2.1 synchronized 同步锁
Java中是为 非公平锁 JVM自解锁
可以 锁整个方法、或者 锁代码块
1.2.2 ReEntrantLock(重入锁)
通过 lock 和 unlock 来进行加锁 与解锁操作 必须要需手动解锁 默认的lock是为非公平锁 可以设置为公平锁
1.2.3 Semaphore 信号量同步
信号无需由同一个线程来获取和释放,因此信号可用于异步事件通知,
信号包含状态,因此可以异步方式使用,而不用象条件变量那样要求获取互斥锁。
信号的 效率 不如 互斥锁 高
1.2.3.1PV原语
PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序
P原语操作的动作是: (1)sem减1; (2)若sem减1后仍大于或等于零,则进程继续执行; (3)若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
V原语操作的动作是: (1)sem加1; (2)若相加结果大于零,则进程继续执行; (3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。
1.2.4 AtomicInteger 类
是一个提供原子操作的Integer 的类,性能会比 ReEntrantLock 好几倍
AtomicBoolean、 AtomicLong、 AtomicReference等;
AtomicReference:可以使用AtomicReference将一个对象的所有操作转化成原子操
1.2.5 可重入锁(递归锁)
外层函数获取到锁后 内层函数仍有获得该锁的代码 但不受影响
Java环境下 ReentrantLock 和 Synchronize 都是可重入锁
1.3 静态代理模式
/** 静态代理模式总结
*
* 真是对象和代理对象都是要实现同一个接口
* 代理对象要代理真实角色
*
* 好处:
* 代理对象可做很多真实对象无法做到的事
* 真实对象专注于自己的业务
*/
public class StacticProxy {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("111");
}
}).start();
new Thread( () -> System.out.println("111")).start();
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
new WeddingCompany(new You()).HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("You Happy Marry!");
}
}
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void after() {
System.out.println("After");
}
private void before() {
System.out.println("Before");
}
}
1.4 Lambda表达式
1.4.1 Lambda好处
- 避免匿名内部类定义过多
- 去掉了没有意义的代码 只留下核心的逻辑
1.4.2 理解函数式接口
-
Functional Interface(函数式接口)是学习Lambda的关键
-
函数式接口的定义
-
任何接口 如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
public interface Runnable{ public abstract void run(); }
-
对于函数式接口,我们可以通过Lambda表达式来创建该接口的对象
-
1.5 sleep()、wait()