1. 线程实现
1.1 线程创建(三种方法)
- Java下载图片的方法:通过
FileUtils.copyURLToFile
方法 - 获取线程名字
Thread.currentThread.getName()
class WebDownloader {
//下载方法
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异常,downloader方法出现问题");
}
}
}
继承Thread类
继承后实现run方法,在main方法中可以new新对象直接调用start方法。
实现Runnable接口 (最重要)
实现run方法后,在main方法中new了新对象,还要将其传入new Thread中,才可以调用start方法。
还可以在newThread传入对象的同时,传入一个字符串表示线程名字。
实现Callable接口
实现Callable接口<Boolean>有返回值,创建目标对象,创建执行服务,提交执行代替.start,获取结果bool,关闭服务。
类 c = new C();
- 创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);
- 提交执行 Future<Boolean> r=ser.submit(c)
- 获取结果 boolean res= r.get();
- 关闭服务 ser.shutdownNow()
1.2 静态代理
真实对象和代理对象都实现同一个接口。
代理对象做真实对象做不了的事,让真实对象专注做自己的事。
// 线程中使用的就是代理模式
public class Demo8_StaticProxy {
public static void main(String[] args) {
new Thread(()-> System.out.println("我爱你")).start();
new WeddingCompany(new You()).happyMarry();
}
}
1.3 Lamda表达式
对于实现函数式接口的类可以被Lamda表达式替代。
函数式接口定义
包含唯一一个抽象方法。
new 接口名({函数方法}); 等价于
(int a,int b) ->{
sout();
}
- 接口实现只有一行代码时可以省略{}
- 可以省略参数类型 ,要省略就必须全省略
- 多个参数必须有()
2.线程的状态
2.1 状态转换
new
-》创建状态
.start
-》就绪状态
.sleep /wait
-》阻塞状态
2.2 线程方法
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程优先级 |
static void sleep(long millis) | 指定毫秒休眠 |
void join | 等待该线程终止 |
static void yield() | 暂停当前 正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程,不推荐~!! |
boolean isAlive() | 检测是否存活 |
停止线程 自己写stop
不推荐stop/destroy等JDK不建议的方法。
不建议使用死循环
建议正常停止,使用标志位。
线程休眠 Thread.sleep(int mills)
- sleep时间到达后进入就绪状态
- 可以模拟网络延时,倒计时
- 每一个对象都有一把锁,Sleep不会释放锁
//模拟时钟
public class Recount implements Runnable{
@Override
public void run() {
Date date = new Date(System.currentTimeMillis());
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(new SimpleDateFormat("YY:MM:dd:HH:mm:ss").format(date));
date=new Date(System.currentTimeMillis());
}
}
public static void main(String[] args) {
Recount recount = new Recount();
new Thread(recount).start();
}
}
线程礼让 Thread.yield()
- 让当前线程暂停,回到就绪状态。
- CPU重新调度,所以礼让不一定成功。
线程插队 对象thread.join()
- 其他线程阻塞等待对象线程执行完毕
- 由其他线程 使用对象线程引用.join()。相当于某线程让别的线程先强制执行。
public class Jointest implements Runnable{
@Override
public void run() {
for (int i=0;i<500;i++) {
System.out.println("vip线程来啦"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Jointest jointest = new Jointest();
Thread vip =new Thread(jointest);
vip.start();
for (int i = 0; i < 200; i++) {
if(i==100) {
vip.join();
}
System.out.println("我是主线程"+i);
}
}
}
观察线程状态 Thread.State
- NEW:刚new出来的线程
- RUNNABLE:执行了statrt,在虚拟机中执行的线程
- BLOCKED:被阻塞的线程
- WAITING:正在等待另一个线程执行特定动作的
- TIMED_WAITING:正在等待到指定时间的线程。
- TERMINATED:已推出的线程。
Thread.State state = thread.getState()
线程优先级 thread.setPriority(int x)
- 默认优先级是5
- 范围1-10
- 常量 Thread.Max_PRIORITY /MIN_PRIORITY
守护线程 thread.setDaemon(boolean )
- 线程分为 用户线程和守护线程
- 虚拟机确保用户线程执行完毕,不用等待守护线程执行完毕
- 常见的守护线程有 后台记录操作日志,监控内存,垃圾回收
- 设置用户线程为守护线程:
thread.setDaemon(true)
3. 线程同步
多线程对同一对象访问,需要线程同步。这是一种等待机制,多个需要访问该对象的线程进入对象的等待池形成队列。
同步的形成条件: 队列+锁
3.1 不安全的情况
由于线程在自己内存处理导致
- 购票
- 取钱
- 集合 (多线程操作同一块。)
3.2 隐式锁 Synchronized
同步方法
在方法中加入关键字,相当于锁this这个对象本身。方法一旦执行,就独占锁直到方法返回,后面被阻塞的线程才能获得锁。
同步代码块 Synchronized(OBJ) {}
OBJ称为同步监视器,将变量/共享资源作为同步监视器可以达到同步的目的。
在原有代码上外套{}即可
JUC安全集合类型扩充
java.util.concurrent包中的
CopyOnWriteArrayList集合是安全的。底层有votilaite关键字。
3.3 显示锁 Lock
- concurrent.locks.Lock是一个接口,线程开始访问共享资源应先获取Lock对象。
- Lock的实现类 可重入锁ReentrantLock,可以显式加锁解锁。
4. 线程通信
4.1 通信方法
都是Object类的方法,只能在同步方法或同步代码块中使用,否则会抛出异常。
方法名 | 作用 |
---|---|
wait() | 线程释放锁并等待 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程。 |
生产者消费者举例
- 容器方法
消费者和生产者各自定义缓冲区,调用容器的方法;缓冲区定义容器大小,定义容器增减方法。 - 信号灯法
消费者和生产者各自定义传递的数据类,调用容器方法。缓冲区通过自定义的flag来控制。
public class Demo33_ThreadPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Producer(synContainer).start();
new Consumer(synContainer).start();
}
}
//生产者
class Producer extends Thread {
//容缓冲区
SynContainer container;
public Producer(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Product(i));
System.out.println("生产了" + i + "件产品");
}
}
}
//消费者
class Consumer extends Thread {
//容缓冲区
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->" + container.pop().id + "件产品");
}
}
}
//产品
class Product {
int id;//产品编号
public Product(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer {
//需要一个容器大小
Product[] products = new Product[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Product product) {
//如果容器满了,需要等待消费者消费
/*如果是if的话,假如消费者1消费了最后一个,这是index变成0此时释放锁被消费者2拿到而不是生产者拿到,这时消费者的wait是在if里所以它就直接去消费index-1下标越界,如果是while就会再去判断一下index得值是不是变成0了*/
while (count == products.length) {
//通知消费者消费,等待生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,需要丢入产品
products[count] = product;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Product pop() {
//判断是否能消费
while (count <= 0) {
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Product product = products[count];
//吃完了 通知生产者生产
this.notifyAll();
return product;
}
}
标签:Java,Thread,void,线程,new,多线程,方法,public
From: https://www.cnblogs.com/its1440/p/17526015.html