JAVA多线程学习

为什么要使用多线程?

  1. 利用多线程提高程序的扩展能力,以达到业务对吞吐量的要求
  2. 协调线程间交互、调度,以完成业务逻辑

多线程付出的代价:
1)设计更复杂
虽然有一些多线程应用程序比单线程的应用程序要简单,但其他的一般都更复杂。在多线程访问共享数据的时候,这部分代码需要特别的注意。线程之间的交互往往非常复杂。不正确的线程同步产生的错误非常难以被发现,并且重现以修复。
2)上下文切换的开销
当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行。这种切换称为“上下文切换”(“context switch”)。CPU会在一个上下文中执行一个线程,然后切换到另外一个上下文中执行另外一个线程。上下文切换并不廉价。如果没有必要,应该减少上下文切换的发生。

多线程的生命周期:
新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态

描述:
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换

新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

    线程对象  别名=new 线程对象();

就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行

运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

    线程别名.start();  启动线程运行状态

阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态

死亡状态,线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。

上代码:

创建线程:
第一种方法:

继承Thread类, Thread实现了runnable接口
public class NewThread  extends Thread {
    @Override
    public void run() {
        System.out.println("运行线程~~");
    }

    public static void main(String[] args) {
        NewThread newThread=new NewThread();
        newThread.start();
    }
}

第二种方法:实现Runnable接口

实现了 runnable接口
public class NewThreadImpl implements Runnable {

    @Override
    public void run() {
        System.out.println("必须要重写run方法");
    }

    public static void main(String[] args) {
        Thread thread=new Thread(new NewThreadImpl());
        thread.start();
    }
}

Synchronized:

Synchronized主要是通过对象锁的方式来实现线程安全(这边的锁是指虚拟机实现的锁,和Java Lock类库不是一个概念),修饰静态方法获取的是Class对象的锁,修饰方法是获取当前对象this的锁,修饰括号内容是获取括号内对象的锁。


Concurrent
Java类库中有专门的concurrent包实现线程安全,核心类就是AQS(AbstractQueuedSynchronizer),一个抽象队列同步器,相当于一个模板,类库中ReentantLock等都是通过它来实现的。

并发容器:
这些容器的关键方法大部分都实现了线程安全的功能,却不使用同步关键字 (synchronized)。值得注意的是 Queue 接口本身定义的几个常用方法的区别,

add 方法和 offer 方法的区别在于超出容量限制时前者抛出异常,后者返回 false;
remove 方法和 poll 方法都从队列中拿掉元素并返回,但是他们的区别在于空队列下操作前者抛出异常,而后者返回 null;
element 方法和 peek 方法都返回队列顶端的元素,但是不把元素从队列中删掉,区别在于前者在空队列的时候抛出异常,后者返回 null。
阻塞队列:

BlockingQueue.class,阻塞队列接口
BlockingDeque.class,双端阻塞队列接口
ArrayBlockingQueue.class,阻塞队列,数组实现
LinkedBlockingDeque.class,阻塞双端队列,链表实现
LinkedBlockingQueue.class,阻塞队列,链表实现
DelayQueue.class,阻塞队列,并且元素是 Delay 的子类,保证元素在达到一定时间后才可以取得到
PriorityBlockingQueue.class,优先级阻塞队列
SynchronousQueue.class,同步队列,但是队列长度为 0,生产者放入队列的操作会被阻塞,直到消费者过来取,所以这个队列根本不需要空间存放元素;有点像一个独木桥,一次只能一人通过,还不能在桥上停留
非阻塞队列:

ConcurrentLinkedDeque.class,非阻塞双端队列,链表实现
ConcurrentLinkedQueue.class,非阻塞队列,链表实现
转移队列:

TransferQueue.class,转移队列接口,生产者要等消费者消费的队列,生产者尝试把元素直接转移给消费者
LinkedTransferQueue.class,转移队列的链表实现,它比 SynchronousQueue 更快
其它容器:

ConcurrentMap.class,并发 Map 的接口,定义了 putIfAbsent(k,v)、remove(k,v)、replace(k,oldV,newV)、replace(k,v) 这四个并发场景下特定的方法
ConcurrentHashMap.class,并发 HashMap
ConcurrentNavigableMap.class,NavigableMap 的实现类,返回最接近的一个元素
ConcurrentSkipListMap.class,它也是 NavigableMap 的实现类(要求元素之间可以比较),同时它比 ConcurrentHashMap 更加 scalable——ConcurrentHashMap 并不保证它的操作时间,并且你可以自己来调整它的 load factor;但是 ConcurrentSkipListMap 可以保证 O(log n) 的性能,同时不能自己来调整它的并发参数,只有你确实需要快速的遍历操作,并且可以承受额外的插入开销的时候,才去使用它
ConcurrentSkipListSet.class,和上面类似,只不过 map 变成了 set
CopyOnWriteArrayList.class,copy-on-write 模式的 array list,每当需要插入元素,不在原 list 上操作,而是会新建立一个 list,适合读远远大于写并且写时间并苛刻的场景
CopyOnWriteArraySet.class,和上面类似,list 变成 set 而已



    public static void main(String[] args) {
        TestThread  mt = new TestThread();
        mt.setName("T_T");
        mt.start();
        try {
            Thread.currentThread().sleep(10);
            mt.park();
            Thread.currentThread().sleep(20);
            mt.unPark();
            Thread.currentThread().sleep(30);
            mt.park();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    static class TestThread  extends Thread {

        private boolean isPark = false;
        @Override
        public void run() {
            System.out.println(" Enter Thread running.....");
            while (true) {
                if (isPark) {
                    System.out.println("Thread is Park.....");
                    LockSupport.park();
                }
            }
        }
        public void park() {
            isPark = true;
        }
        public void unPark() {
            isPark = false;
            LockSupport.unpark(this);
            System.out.println("Thread is unpark.....");
        }
    }

线程使用sheep

 public static void main(String[] args)throws InterruptedException{
        int i=0;
        boolean iswhile=true;
        while (iswhile) {
            Integer integers = (int) (100 * Math.random());
            String ss = SendGET("https://blog.youkuaiyun.com/iteye_9328/article/details/102821001", String.valueOf(integers));
            System.out.println(ss);

            synchronized(new Object()){   //自行处理
                i = i + 1;
                Thread.sleep(60000);
                if (i == 300) {
                    iswhile = false;
                }
            }
        }
    }

    public static String SendGET(String url,String param){
        System.out.println("访问的url:"+url+"?"+param);
        String result="";//访问返回结果
        BufferedReader read=null;//读取访问结果
        try {
            //创建url
            URL realurl=new URL(url+"?"+param);
            //打开连接
            URLConnection connection=realurl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            //建立连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
            // 遍历所有的响应头字段,获取到cookies等
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            // 定义 BufferedReader输入流来读取URL的响应
            read = new BufferedReader(new InputStreamReader(
                    connection.getInputStream(),"UTF-8"));
            String line;//循环读取
            while ((line = read.readLine()) != null) {
                result += line;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(read!=null){//关闭流
                try {
                    read.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值