高并发与多线程第一篇-线程

一、线程的概念

一个完整的进程中,一个代码的实现路径就是一个线程。

二、线程的创建方式

线程创建方式有两种:一种是继承Thread类,重写run()。或者是实现Runnable接口,重写run方法。
public class Demo02 extends Thread{

    @Override
    public void run() {
        System.out.println("需要在多线程下执行的代码【Thread】");
    }

    public static void main(String[] args) {
        new Demo02().start();
    }
}
class Demo03 implements Runnable{
    @Override
    public void run() {
        System.out.println("需要在多线程下执行的代码【Runnable】");
    }
    public static void main(String[] args) {
        new Thread(new Demo03(),"线程").start();
    }
}
当然在这两种的基础上,发展出其他的线程创建方式
例如:实现Callable接口,一般在使用callable时会使用FutureTask来包装callable
/**
 *Callable接口中重写call方法,可以获取返回值
 */
class Demo04 {
    public static void main(String[] args) {

        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return 100;
            }
        });
        new Thread(task).start();
        try {
            System.out.println("接口到的Callable的返回值:"+task.get());
        }catch (Exception e){
            e.getStackTrace();
        }

    }

}
例如:使用线程池的方式创建线程
class Demo05{
    public static void main(String[] args) {
        try {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
            for (int i=0;i<10;i++){
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+"线程开始执行");
                    }
                });
            }
        }catch (Exception e){
            e.getStackTrace();
        }

    }
}
线程的创建方式有很多种,但是都是基于Thread类和Runnable接口在做进一步的实现。
如果在进一步的探讨线程写法上不实现,还有lamda表达式的一种线程创建方式
class Demo06 {

    public static void main(String[] args) {
        new Thread(()-> System.out.println("线程开始执行")).start();
    }
}
这种方式只是写法上的不同,其基本的实现还是通过Runnable进行实现的

三、线程的状态

线程的状态共分为三种状态,只是在运行状态中,根据线程是否拥有CPU执行权,分为就绪状态和运行中状态
    new          新建状态  Thread t =new Thread() 线程就会进入新建状态
    runnable     运行状态  t.start() 进入运行状态
        ready    就绪状态  在等待cpu执行权时是就绪状态
        running  运行中状态 获取到cpu执行权为运行中状态
    blocked      阻塞状态  线程在等待获取锁时为阻塞状态
    teminated    结束状态  线程结束销毁时就是结束状态

class Demo07 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("线程状态:"+Thread.currentThread().getState()));
        System.out.println("线程状态:"+thread.getState());
        thread.start();
        System.out.println("线程状态:"+thread.getState());
    }
}

可以使用这种方式是测试线程的状态,并且在实际的多线程场景中,由于实际需求,线程调用sleep()或者yield()等方法线程进入就绪状态,或者是代码加同步锁synchronized线程进入阻塞状态。可以根据实际的需求进行编写。

四、线程常用的方法

Tread.currentThread()    获取当前线程对象
getPriority()            获取当前线程的优先级
setPriority()            设置当前线程的优先级,线程的优先级高,被cpu调度的概率会变大,但不是绝对会被优先调度,也有一定几率会运行优先级较小的线程。
isAlive()                判断线程是否存活
join()                   在A线程中调用线程B.join(),就在先执行B线程,执行完之后才会继续执行A线程
sleep()                  让线程休眠指定的毫秒数,进入阻塞状态,线程超时会自动苏醒进入就绪状态。sleep()方法是Thread的方法,可以在任意位置进行调用,注意:sleep()是不会释放锁的
wait()                   线程等待,进入阻塞状态,wait()方法是Object的方法,必须在同步锁代码块内调用,会释放锁
notify()                 唤醒wait()阻塞的当前线程,必须在同步锁代码块内调用
notifyAll()              唤醒所有被wait()阻塞的线程,必须在同步锁代码块内调用
yield()                  线程让出cpu的执行权,返回就绪状态从新竞争cpu

五、线程锁

synchronized 同步锁,在sync修饰代码块后,这个代码块只能由一个线程进行访问,sync锁定的是对象
    sync(Object) 锁定对象Object
    sync method() 修饰方法时,锁定的是this
    sync static method() 修饰静态方法时,锁定的是T.class
    在多线程场景下,对于部分操作必须一次只能由一个线程进行操作,所以就必须在这一部分代码上加sync进行修饰,例如:
class Demo08 {

    static final Object o = new Object();

    private static int count = 0;

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    m();
                }
            });
        }
        service.shutdown();
    }
    static void m() {
        synchronized (o) {
            count++;
            System.out.println(count);
        }

    }
}
    在以上的示例中count++在多线程中并不是一个线程安全的操作,所以必须要用sync进行修饰。这个需要注意一个点,sync锁定的对象必须是唯一的一个,所以需要在加锁的对象上加锁final修饰。
    synchroized的所升级的过程:
        偏向锁:只有一个线程的时候,会在锁定对象的markword中标记这个线程的ID,主要是用来确认当前线程的数量
        自旋锁:当第二个线程来请求锁定对象时,sync会自动升级为自旋锁,也就是CAS模式。
        重量级锁:当在自旋锁中,某一个线程自旋次数超过十次,锁就会自动升级为重量级锁,也就是会进入到内核中加锁。
        注意:这里需要注意一点锁升级之后是不能降级的
    锁的选择:
        锁定代码执行时间长,线程数多,则选用sync
        锁定代码执行时间短,线程数少,则选用automic,auto选用的也就是CAS锁的实现
volatlie  修饰的变量在多线程中主要是为了保证多线程之间变量的可见性,同时防止jvm对于变量的指令进行重新排序。
CAS(Compare And Set)的大致实现:

class Demo09 {

    private int count = 0;
    /**
     * 以下示例只是为方便理解写的伪代码,不要复制后去执行
     * e:count的实际值
     * v:count的期望值
     * n:count的新值
     */
    static void cas(int e, int v, int n) {
        //判断count实际的值是否与期望的值相等,如果相等表示count没有被别的线程修改,则本线程进行修改操作
        if (e == v) {
            e = n;
        } else {
            //如果count实际的值是否与期望的值不相等,则重新获取count最新值,再进行cas操作
            v = e;
            Demo09.cas(e, v, n);
        }
    }

}

CAS支持CPU源语层面的操作,也就是比如上面的cas()方法,一旦一个线程开始执行这个方法内部代码,
那就不会被打断。所以在执行方法内部代码时不用考虑线程安全的问题,CAS在java.util.current中的实现就是atomic包下

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值