1.进程 线程 并行 并发
进程:正在执行的程序段
线程:共享进程的堆和方法区,程序计数器和虚拟机栈每个线程各有一个,一个进程有多个线程
一个java应用程序至少有3个进程:main()主线程 gc()垃圾回收线程 异常处理线程
并行:多个cpu同时执行多个任务
并发:一个CPU采用时间片"同时"执行多个任务
多线程的优点:
-
提高应用程序的响应 对图形化界面更有意义,可增强用户体验
-
提高CPU利用率
2.创建线程与使用
java.lang.Thread
创建1.继承Thread类,重写run函数
创建实例,然后实例 . start():启动线程 并调用run方法
只用run方法的话不会新建线程,还是在总main()线程
Thread.currentThread,getName()获取当前进程的名字
对一个线程来说,只能start一次,不然会报错 只能新new一个
new Thread(){
public void run(){
}}
匿名类,重写run方法
currentThread() : 静态方法,返回执行当前代码的线程
getName()获取线程名字
setName() 设置线程名字 在启动前使用
yield() : 释放当前线程的执行权 但有可能下次又抢到了执行
join() : 如h1.join() 直到h1进程执行完之后原线程才会结束阻塞状态
stop() : 结束一个线程 该方法已过时,不建议使用
sleep(): 进程进入阻塞状态 参数为阻塞的时间
isAlive():判断进程是否还存活
创建2:实现接口Runnable
如果以一个实现类来创建几个进程的话,几个进程共享数据,可以实现共享数据不用static
Thread也实现了Runnable接口,本质上创建一个线程就是实现接口类里面的run方法
线程同步
某个线程对共享变量还没操作结束另一个进程也要读取、修改共享变量,这时就会出现问题
如何解决:当一个线程操作共享数据时,其他线程不能参与
操作不止包含修改删除,还有调用也是
同步机制:
方法一:
同步代码块:
//处理Runnable实现的方式
synchronized(同步监视器){
//需要同步的代码:操作共享数据的代码
}
//同步监视器:锁,任何一个类的对象都可以充当锁 多个线程共用一个锁
//处理Thread继承方式:
//多个进程要创建n个实例对象,所以在初始化锁时,要加上static
//锁可以用this 指向当前对象 不能用于继承方式
//可以用window.class window是类名
方法二:
同步方法
如果操作共享数据的代码完整地声明在一个方法中,把这个方法定义为同步的
//解决Runnable
public synchronized coid show(){//同步监视器是this
//代码与同步代码块内的代码相同
}
//解决继承Thread
public static synchronized void show(){//锁是类 window.class
//保证内部函数也是静态的 Thread.currentThread.getName
}
单例模式 懒汉式
//使用同步机制将单例模式中的懒汉式改写为线程安全的
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
//多个线程都调用该函数时,有可能会出现安全问题
//处理方法:1 public static synchronized Bank getInstance()
// 2 synchronzied (Bank.class){
// 里面是if else 内容
// }
/*if(instance==null){
synchronized (Bank.class){
if(instance == null){
instance = new Bank();
}
}
}
return instance;*/
if(instance==null){
instance = new Bank();
}
return instance;
}
}
方式三
lock锁 jdk5新增
class Window implements Runnable(){
private int ticket = 100;
//实例化
//可设置参数为true: private ReentrantLock lock = new ReentrantLock(true);一个线程解锁后不会立即又一次将资源分配给该进程
private ReentrantLock lock = new ReentrantLock();
//如果是继承的方式 lock要加上static
public void(){
while(true){
try{
lock.lock();
}finally{
lock.unlock();
}
}
}
}
lock与synchronized不同
synchronized :自动释放同步监视器;有代码块,也有方法锁;隐式锁
lock: 需要手动启动、结束同步;只有代码块锁;JVM花费较少时间调度线程;性能更好;显式锁
lock > 同步代码块 > 同步方法
进程通信
wait() 使进程阻塞,该线程一阻塞,会释放锁,而sleep不会
notify()一旦执行会唤醒wait的优先级最高的那个线程
notifyAll() 唤醒所有
- 必须放在同步代码块或同步方法中
- 这三个方法的调用者必须是同步代码块或同步方法中的同步监视器
- 这三个方法定义在object类中
sleep与wait:
相同:都会使线程阻塞
不同:
-
Thread类声明sleep() Object声明wait
-
sleep可以在任何场景下 wait在同步代码块或同步方法
-
都在同步代码块或同步方法中 sleep不用释放锁 wait需要
生产者、消费者问题
class clert{
private int number=0;
public void add(){
number = number+1;
}
public void seq(){
number = number - 1;
}
}
class producer extends Thread{
private clert clerk;
public producer(clert clerk){
this.clerk = clert;
}
//用同一个clerk初始化该类
public void run(){
while(true){
synchronized(){
if(a.)
}
}
}
}
class Consumer extends Thread{
private clert clerk;
public Comsumer(clert clerk){
this.clerk = clert;
}
public void run(){
while(true){
synchronized(){
if(a.number>0)
{
a.seq();
}
}
}
}
}
jdk 5.0新增线程创建方式
-
实现Callerable接口:可抛出异常、有返回值、支持泛型返回
- 创建一个实现Callable的实现类
- 实现call函数,线程完成的任务放在call函数中
- 创建Callable实现类的对象
- 将此对象传递给FutureTask构造器中
- 将FutureTask对象传到Thread构造器中
class myThread implements Callable{ public Object call() throws Exception{ int sum=0; return sum; } } public class A{ psvm{ myThread a = new myThread(); FutureTask b = new FutureTask(a); new Thread(b).start(); //FutureTask是唯一实现Future接口的实现类,同时也实现了Runnable,可以获得取消、获取 实现Callable和Runnable类 执行结果 Object sum = b.get();//要try catch } }
-
使用线程池: 提前创建多个线程,放入线程池,使用时直接获取,使用完放回池中
提高响应速度
降低资源消耗
便于线程管理
corePoolSize
maximumPoolSize
keepAliveTime
public class B{ psvm(){ ExecutorService service = Executors.newFisedThreadPool(10); //有10个进程的进程池 service.execute();//参数为Runnable实现类 service.submit();//参数为Callable实现类 有返回值 service.shutdown();//关闭线程池 } }