线程API
线程相关的若干方法
Thread.currentThread方法
.Thread类的静态方法currentThread方法可以用来获取运行当前代码片段的线程
Thread current = Thread.currentThread();
获取线程相关信息的方法
String getName();返回该线程的名称
long getId();返回该线程的标识符
int getPriority:返回该线程的优先级
Thread.state getState:获取线程的状态
boolean isDaemon():测试当前线程是否为守护线程
测试currentThread()方法:
public static void main(String[] args) {
System.out.println("运行main方法的线程是:"+Thread.currentThread());
TestCurrent();
}
public static void TestCurrent() {
// 实现输出当前线程
System.out.println("运行currentThread方法的线程是:"+Thread.currentThread());
}
Thread[main,5,main]:表示当前线程的名字为main,优先级为5,当前线程的主线程为main
在内部类中查看当前线程.
System.out.println("运行main方法的线程是:"+Thread.currentThread());
TestCurrent();
Thread t = new Thread() {
@Override
public void run() {
System.out.println("线程t"+Thread.currentThread());
TestCurrent();
}
};
t.start();
问题:测试线程的相关方法
String getName();返回该线程的名称
long getId();返回该线程的标识符
1.创建两个线程,输出默认的线程名和默认的ID
2.创建一个线程,设置线程的名字并输出线程名和默认的ID
Thread t1 = new Thread();
System.out.println(t1.getName());
System.out.println(t1.getId());
System.out.println(t1.getState());
Thread t3 = new Thread("nihao");
//t3.setName("t3的线程名");
System.out.println(t3.getName());
线程的优先级:
.线程的切换是有线程的调度控制的,我们无法通过代理来干涉,但是我们可以通过提高线程
的优先级来最大程度的改善线程获取时间片的概率
线程的优先级被划分成1-10级,值分别是1-10,其中1最低,10最高,线程提供了3个常量表示
最低,最高一级默认值
Thread.MAX_PRIORITY 10
Thread.MIN_PRIORITY 1
Thread.NORM_PRIORITY 5
-void setPriority();
设置线程的优先级
守护线程
.守护线程与普通线程在表示上没有什么区别,我们只需要通过Thread提供的方法设定即可:
-void setDaemon(boolean);
-当参数为true的时候该线程为守护线程
.守护线程的特点:
当进程中只剩下守护线程时,所有的守护线程限制终止
.GC就是运行在守护线程上.
测试守护线程
问题:
1.使用内部类创建线程的方式创建线程d,该线程实现每隔0.1s输出字符串"后台进程"
2.设置线程d为守护线程
3.使main线程阻塞5s,然后输出字符串"main线程结束"
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
while(true) {
int num = 100;
System.out.println(num);
// 使线程休眠0.1s
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// 设置线程t为守护线程
t.setDaemon(true);
// main线程调用线程t
t.start();
// 阻塞main线程5s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main线程结束了");
}
sleep方法:
Thread的静态方法sleep用于使当前线程进入阻塞状态
-static void sleep(long ms)
.该方法会使当前线程进入阻塞状态指定毫秒,当阻塞指定毫秒后,当前线程会重新进入
Runnable状态,等待分配时间片
.该方法声明抛出一个InterruptedException.所以在使用该方法时需要捕获这个异常
*(了解)
yield方法:
Thread的静态方法yield:
-static void yield()
.该方法用于使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片
join方法
Thread的方法join
.该方法用于等待当前线程结束
.该方法声明抛出异常InterruptedException
测试join方法
使用两个线程模拟图片下载的过程
1.创建线程t1,该线程模拟实现图片的下载过程,即在该线程中实现输出字符串
"t1:正在下载图片"+下载的百分数
2.创建线程t2,在该线程中,首先输出"t2:等待图片下载完毕";然后将t1线程作为t2的
子线程,最后输出"t2:显示图片"
3.启动线程
4.一定要t1先执行完,之后才会显示"t2:显示图片"
public static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("t1正在下载图片:"+i*10+"%");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1图片下载完毕");
}
};
Thread t2 = new Thread() {
@Override
public void run() {
System.out.println("等待图片下载完毕");
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2:显示图片");
}
};
t1.start();
t2.start();
}
多线程基础
线程同步
StringBuffer是同步的synchronized append();
StringBuilder不是同步的append();
.ArrayList与HashMap不是同步的
.Vector和HashTable是同步的
// 如何获取线程安全的集合
Collections.synchronizedList(null);
// 如何获取线程安全的Map
Collections.synchronizedMap(null);
重点(线程池)
线程池的作用
-控制线程数量(防止系统因过度开辟新线程导致的系统崩溃)
-重用线程(避免系统资源浪费,以及过度切换线程所带来的隐患)
.当一个程序中若创建大量线程,并在任何解释后销毁,会给系统带来过度消耗资源
以及过度切换线程所带来的危险,从而导致系统崩溃,为此我们使用线程池来解决这个问题
.使用ExecutorService提供的用于管理线程池的类
线程池的概念:首先创建一些线程,他们的集合称为线程池,当服务器收到一个客户请求后,
就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到
线程池中
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到
任务后,他就在内部找有无空闲的线程,再把任务交给内部某个空闲的线程
ps:一个线程同时只能执行一个任务,但是可以同时向一个线程池提交多个任务.
编程模式下创建一个线程池.
1.Executors.newFixedThreadPool(int nThreads);
创建一个可重用固定线程集合的线程池
2.Executors.newFixedThreadPool();
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用他们
3.有界队列 当有界队列也满了
测试使用ExecutorService实现线程池
1.线程池要执行的任务为每隔一秒输出当前线程的名字,总计输出10次
2.创建一个线程池,该线程池只有两个空闲线程
3.是线程池执行5次步骤一的的任务
public class TestExecutoService {
public static void main(String[] args) {
// 2.创建一个线程池
ExecutorService threadpool = Executors.newFixedThreadPool(2);
// 3.设置线程池要执行的任务
for (int i = 0; i<5; i++) {
Handle handle = new Handle();
threadpool.execute(handle);
}
}
}
//1.创建handle类,实现runnable接口,创建要执行的任务
class Handle implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println("执行当前任务的线程为:"+name);
for (int i = 1; i <= 10; i++) {
System.out.println(name+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+":执行完毕");
}
}
双缓冲队列:
BlockingQueue
.在多线程并发时,若需要使用队列,我们使用Queue,但是要解决一个问题就是同步,
同步操作会降低并发对于Queue
BlockingQueue内部使用两条队列,可以允许两个线程同时进行操作,在保证并发安全的同时
提高了队列的存储效率
Queue ----先进先出 一端取出,一端存储
ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明
其大小,其所含的对象是以先入先出顺序排序的.
练习:
1.如何创建双缓冲序列
2.如何对双缓冲存取数据
public static void main(String[] args) {
testPull();
}
public static void testOffer() {
// 创建指定大小的双缓冲序列
BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);
// 使用offer方法向队列中添加元素
for(int i = 0;i<20;i++) {
// 设置1秒超时.1s元素仍然没有入队到队列中,则返回false
try {
boolean b = queue.offer(i,1,TimeUnit.SECONDS);
System.out.println("存入是否成功:"+b);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 因为双缓冲的大下为10,所以入队10个元素之后,则不能在原元素入队了
// 测试pull方法
public static void testPull() {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);
// 赋值
for (int i = 0; i < 10; i++) {
queue.offer(i);
}
// 取值
for (int i = 0; i < 20; i++) {
try {
Integer num = queue.poll(1, TimeUnit.SECONDS);
System.out.println("元素:"+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}