线程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();
                }
            }
        }
 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐