线程

 

轻量进程,程序执行流的最小单元。

 

线程有3个基本状态:执行、就绪、阻塞
线程有5种基本操作:派生、阻塞、激活、 调度、 结束

 

线程三大特性
多线程有三大特性,原子性、可见性、有序性
原子性:保证数据一致性,线程安全。
可见性:对另一个线程是否课件
有序性:线程之间执行有顺序

 

创建多线程有两种方法:
1.继承Thread,重写run方法

2.实现Runnable接口,重写run方法

3.实现 Callable 接口;(忽略)

 

一般我们使用实现Runnable接口,Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口。可以避免java中的单继承的限制,应该将并发运行任务和运行机制解耦,因此我们选择实现Runnable接口这种方式。

 

run():仅仅是封装被线程执行的代码,直接调用是普通方法

start():首先启动了线程,然后再由jvm去调用该线程的run()方法。

线程状态:

 方法:

获取线程名称: getName();

线程的强制运行:            .join() 方法         在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。

线程的休眠:              .sleep() 

中断线程:                .interrupt()               如果该线程处于阻塞、限期等待或者无限期等待状态,那么就会抛出 InterruptedException,从而提前结束该线程

线程的礼让               .yield()                   声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行

等待                          .wait()

唤醒                          notify() 或者 notifyAll()

 setDaemon()         守护线程  :只要任何非守护线程还在运行,程序就不会终止(ex: thread.setDaemon(true)

 

wait() 和 sleep() 的区别:

  • wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
  • wait() 会释放锁,sleep() 不会。

 

 

同步和异步:

同步和异步通常用来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者可以继续后续的操作。

 

Java多线程加锁机制,有两种:

  • Synchronized
  • 显式Lock 

 

两种锁机制来控制多个线程对共享资源的互斥访问:

互斥同步:两种方法

1.JVM 实现的 synchronized

2.JDK 实现的 ReentrantLock  (Lock接口的实现类:ReentrantLock)

 

 synchronized:

(1).同步代码方法

            synchronized 方法返回值 方法名称(参数列表){}

public synchronized void func () {
    // ...
}

(2).同步代码块synchronized () { }解决

好处是解决多线程安全问题但是弊端是多个线程需要判断锁,较为消耗资源

ex

public void func() {
    synchronized (this) {
        // ...try } }

synchronized是一种互斥锁(一次只能允许一个线程进入被锁住的代码块),synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。

synchronized用处是什么?

synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)

synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是可见的)

一般我们用来修饰三种东西:修饰普通方法、修饰代码块、修饰静态方法

 

(3)案例:线程安全的懒汉式  DLC

/*private modeltwo() {
}

private static volatile modeltwo m = null;

private static modeltwo getInstanse() {
if (m == null) {
synchronized (modeltwo.class) {
if(m==null)
m = new modeltwo();
}
}
return m;

}*/

 

 

同步函数的锁是this

静态同步函数的锁是class

 

ReentrantLock :        

ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,Lock接口的实现类:ReentrantLock

public class LockExample {

    private Lock lock = new ReentrantLock(); public void func() { lock.lock(); try { for (int i = 0; i < 10; i++) { System.out.print(i + " "); } } finally { lock.unlock(); // 确保释放锁,从而避免发生死锁。 } } }

 

 

 

死锁原因以及解决方法

91)线程之间交错执行

解决:以固定的顺序加锁

(2)执行某方法时就需要持有锁,且不释放

解决:缩减同步代码块范围,最好仅操作共享变量时才加锁

(3)永久等待

解决:使用tryLock()定时锁,超过时限则返回错误信息

 

 

 

J.U.C 一个包,中包含AQS

AQS:LOCK基于AQS实现,AQS是一个给予实现同步锁、同步器的一个框架

 

    

线程池:

线程池可以看做是线程的集合,线程池给这个请求分配一个空闲的线程,任务完成后回到线程池中等待下次任务(而不是销毁)。这样就实现了线程的重用

用的最多的一种线程池:ThreadPoolExecutor

 

 

 

 

三个同步工具类https://segmentfault.com/a/1190000015785789

(1)CountDownLatch(闭锁)

某个线程等待其他线程执行完毕后,它才执行(其他线程等待某个线程执行完毕后,它才执行

(2)CyclicBarrier(栅栏)

一组线程互相等待至某个状态,这组线程再同时执行。

(3)Semaphore(信号量)

控制一组线程同时执行

 

(1)

CountDownLatch(闭锁)

ex:

public static void main(String[] args) {

      Final CountDownLatch countDownLatch =new CountDownLatch(5);

      System.out.println("a");

//启动

      new Thread(new Runnable() {

             public void run() {

                try{

                     countDownLatch.await();

                  }catch(Exception e){

                      e.printStackTrace();

                 }

                 System.out.println("b");

          }

      }

   ).start();

   for(int x=0;x<5;x++){

       new Thread(new Runnable(){

          public void run() {

         System.out.println("c");

            countDownLatch.countDown();

        }

      }

   ).start();

 }

}

 

(2)CyclicBarrier

public static void main(String[] args) {

   final CyclicBarrier cyclicBarrier = new CyclicBarrier(2);

     for(int i=0;i<2;i++){

     new Thread(new Runnable() {

     public void run() {

     String name = Thread.currentThread().getName();

      if (name.equals("Thread-0")) {

        name="a";

      } else {

       name="b";

   }

        System.out.println(name+"-c");

try {

   cyclicBarrier.await(); 

System.out.println("跟" + name + "去夜上海吃东西~");

cyclicBarrier.await();

System.out.println("跟" + name + "去夜上海吃东西~");

cyclicBarrier.await();

System.out.println("跟" + name + "去夜上海吃东西~");

} catch (InterruptedException e) {

e.printStackTrace();

} catch (BrokenBarrierException e) {

e.printStackTrace();

}

}

}).start();

}

}

 

(3)Semaphore

public static void main(String[] args) {

int count = 20;

final Semaphore semaphore = new Semaphore(10);

for (int i = 0; i < count; i++) {

new Thread( new Runnable() {

public void run() {

try{

semaphore.acquire();

System.out.println("a");

Thread.sleep(1000);

System.out.println("b");

semaphore.release();

}catch(Exception e){

e.printStackTrace();

}

}

}).start();

}

}

 

 线程安全:

线程安全有以下几种实现方式:

1.不可变

不可变(Immutable)的对象一定是线程安全的,不需要再采取任何的线程安全保障措施

(final 关键字修饰的基本数据类型、String、枚举类型。。。)

 

2.互斥同步

synchronized 和 ReentrantLock。

 

3.非阻塞同步(CAS、AtomicInteger、ABA )

4.无同步方案(栈封闭、线程本地存储、可重入代码)

 

 

 

多线程开发良好的实践

  • 给线程起个有意义的名字,这样可以方便找 Bug。

  • 缩小同步范围,从而减少锁争用。例如对于 synchronized,应该尽量使用同步块而不是同步方法。

  • 多用同步工具少用 wait() 和 notify()。首先,CountDownLatch, CyclicBarrier, Semaphore 和 Exchanger 这些同步类简化了编码操作,而用 wait() 和 notify() 很难实现复杂控制流;其次,这些同步类是由最好的企业编写和维护,在后续的 JDK 中还会不断优化和完善。

  • 使用 BlockingQueue 实现生产者消费者问题。

  • 多用并发集合少用同步集合,例如应该使用 ConcurrentHashMap 而不是 Hashtable。

  • 使用本地变量和不可变类来保证线程安全。

  • 使用线程池而不是直接创建线程,这是因为创建线程代价很高,线程池可以有效地利用有限的线程来启动任务

 

 

 

 ThreadLocal:

ThreadLocal设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题

ThreadLocal 的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个
线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据

 

转载于:https://www.cnblogs.com/StingLon/p/9798539.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值