JAVA多线程的个人理解

目录

1、进程与线程

        1、进程

        2、线程

2、线程的五种状态

3、自定义线程实现方式

        1、继承Thread类                 

        2、实现Runnable接口

                   1、run方法调用和start调用的区别

        3、实现Callable接口

        4、使用线程池

        5、使用匿名类

4、线程调度

        1、setPriority

        2、sleep

        3、join

        4、yield

        5、等待和唤醒

5、线程同步

        1、同步方法

        2、同步代码块

6、LOCK锁

        1、synchronized与lock的区别

        2、死锁

                1、什么是死锁

                2、产生死锁的条件

                3、死锁实例

7、并发包

        1、ConcurrentHashMap

        2、CountDownLatch

        3、CyclicBarrier

        4、Semaphore

        5、Exchanger


1、进程与线程

        1、进程

                操作系统中的最小单位。通过进程可以创建程序。

        2、线程

                线程是进程中的最小单位,一个进程中可以包含多个线程,并且可以同时执行,从而形成了多线程状态。

2、线程的五种状态

        新建-->就绪-->运行-->阻塞-->死亡

3、自定义线程实现方式

        1、继承Thread类                 

//1、创建一个继承Thread类的子类
public class TestThread extends Thread{
    //2、重写run方法
    @Override
    public void run() {
        for(int i = 0 ; i < 50 ; i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}




public class Test {
    public static void main(String[] args) {
        //3、创建Thread类的子类的对象
        TestThread thread = new TestThread();
        //4、调用strat()方法,从而调用线程的run方法,不能直接调用run方法,因为
        //这样只是简单的方法调用,并未创建线程
        //5、启动线程
        thread.start();


    }
}

                优点:继承方式,优点明显,编程方式比较简单,直接就可以调用

                缺点:单继承,扩展时困难

        2、实现Runnable接口

//创建一个实现了Runnable接口的类
public class TestRunnable implements Runnable{
    //实现接口中的run方法
    @Override
    public void run() {
        for(int i = 0 ; i < 50 ; i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}


public class Test {
    public static void main(String[] args) {
        //创建实现类的对象
        TestRunnable testRunnable = new TestRunnable();
        //将对象作为参数传递到Thread类的构造器中,创建Thread类的对象,并对其命名
        Thread t1 = new Thread(testRunnable,"线程1");
        //通过Thread类的对象调用start(),从而调用当前线程的run()
        t1.start();
        Thread t2 = new Thread(testRunnable,"线程2");
        t2.start();
    }
}

                优点:先将线程类进行实例化,再利用Thread实现线程的 调用,稍显复杂。 更高灵活度,可以实现继承的同时,还可以实现多接口。 可以实现任务共享机制。

                   1、run方法调用和start调用的区别

                                run方法是定义在线程类中,作为自定义线程,需要重写run方 法,如果直接通过main主线程调用,就和调用普通的对象方法是一 样的,并不会让线程单独启动。而start方法是定义在Thread类中 的,通过Start方法,jvm虚拟机会自动的调用run方法,执行线程任 务,所以该种方式是能够实现多线程的方式。一个线程不能够重复 执行。

        3、实现Callable接口

//创建一个实现Callable的实现类
public class TestCallable implements Callable {
    //实现call方法,此线程需要在call()中执行
    @Override
    public Object call() throws Exception {
        int sum = 0;
        //把50以内的相加
        for (int i = 1; i <= 50; i++) {
                System.out.println(i);
                sum += i;
        }
        return sum;
    }
}


public class Test {
    public static void main(String[] args) {
        //创建Callable接口实现类的对象
        TestCallable testCallable = new TestCallable();
        //将此接口实现类的对象传递到FutureTask中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(testCallable);
        //将FutureTask的对象传递到Thread类中,创建Thread对象
        new Thread(futureTask).start();
        try{
            //获取Callable中call方法的返回值,get()即为此方法
            Object sum = futureTask.get();
            System.out.println(sum);
        }catch (InterruptedException e){
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

               优点:call()可以有返回值的,可以抛出异常,被外面的操作捕获,获取异常的信息。Callable是支持泛型的

        4、使用线程池

public class TestExcutors implements Runnable{
    @Override
    public void run() {
        for(int i = 0 ; i < 50 ; i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}


public class TestExcutors1 implements Runnable
{
    @Override
    public void run() {
        for(int i = 0 ; i < 50 ; i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}


public class Test1 {
    public static void main(String[] args) {
        //提供指定线程数量的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //输出class java.util.concurrent.ThreadPoolExecutor
        System.out.println(executorService.getClass());
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) executorService;
        //执行指定的线程操作,需要提供实现Runnable接口或Callable接口实现类的对象
        service1.execute(new TestExcutors());//适用于Runnable
        service1.execute(new TestExcutors1());
        //service1.submit(Callable callable);//适用于Callable
        service1.shutdown();
    }
}

                优点:1.提高响应速度(减少了创建新线程的时间)2.降低资源消耗(重复利用线程池中线程)3.便于线程管理

        5、使用匿名类

Thread thread = new Thread(new Runnable() {
	@Override
	public void run() {
		// 线程需要执行的任务代码
		System.out.println("子线程开始启动....");
		for (int i = 0; i < 50; i++) {
			System.out.println("run i:" + i);
		}
	}
});
thread.start();

4、线程调度

        1、setPriority

                        更改线程的优先级,优先级是从1~10设置,默认是5.

 Thread thread1 = new Thread(tr1,"线程优先级
高") ;
 //设置线程的优先级
        thread1.setPriority(10);
        System.out.println(thread1.getPriority());
        thread1.start();

        2、sleep

                        能够让当前线程进入休眠状态,通过毫秒的参数进行设定。

public class SleepTest extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "=" + i);
        }
    }
    public static void main(String[] args) {
        SleepTest st = new SleepTest() ;
        //调用
        st.start();
        SleepTest st1 = new SleepTest() ;
        //调用
        st1.start();
        SleepTest st2 = new SleepTest() ;
        //调用
        st2.start();
        SleepTest st3 = new SleepTest() ;
        //调用
        st3.start();
    }
}

        3、join

                        让一条线程强制插入到正在运行的线程过程中,在执行后,再让出执行权。

public void sleep(){
        while (true) {
            //每秒睡一次
            try {
                Thread.sleep(1000);
           } catch (InterruptedException e) {
                throw new RuntimeException(e);
           }
            Date date = new Date();
            System.out.println(date);
       }
 }


//main方法:

for (int i = 0; i < 10; i++) {
          
    System.out.println(Thread.currentThread().getName() + "=" + i);
    if(i == 5) {
        try {
                  st.join(); //强势性,插入执行后,其他线程全部等待
            } catch (InterruptedException e){
                  throw newRuntimeException(e);
        }
     }
}

        4、yield

                线程礼让,暂停当前正在执行的线程对象,让其他线程运行, 但是不保证一定会礼让。

for (int i = 0; i < 10; i++) {
          
    System.out.println(Thread.currentThread().getName() + "=" + i);
    
    if(i == 5) {
          Thread.yield();
    }

}

        5、等待和唤醒

                拿包子铺和包子的案例进行说明

//此代码写的是死循环,需自己点击暂停
package com;

import java.util.List;

public class BunStore extends Thread{
    private List<String> list;
    public BunStore(List<String> list){
        this.list = list;
    }

    @Override
    public void run() {
        while(true){
            synchronized (list){
                if(list.size() > 0){
                    try {
                        //存元素的线程进入到等待状态
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果线程没进入到等待状态 说明集合中没有元素
                //向集合中添加元素
                list.add("生产了一个包子。。。。");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者生产了一个包子");
                //集合中已经有元素了 唤醒获取元素的线程
                list.notify();
            }

        }
    }
}



package com;

import java.util.List;

public class Customer extends Thread{
    private List<String> list;
    public Customer(List<String> list){
        this.list = list;
    }

    @Override
    public void run() {
        while(true){
            synchronized (list){
                if(list.size() == 0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("顾客吃了一个包子");
                list.remove(0);
               try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                list.notify();
            }

        }
    }
}



import com.BunStore;
import com.Customer;

import java.util.ArrayList;
import java.util.List;

public class EatBunTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        BunStore bunStore = new BunStore(list);
        Customer customer = new Customer(list);
        bunStore.start();
        customer.start();
    }
}


5、线程同步

        当多个线程共享同一资源时,可能会造成线程安全问题,因此,Java提供了一个关键字synchronized。该关键字能够修饰方 法,被称为同步方法,如果对某一段代码进行修饰和包围,代表是一个同步代码块。由于采用同步机制,所以线程会造成排队的情 况,导致程序性能下降。因此只能是在一些带有“伤害性”的代码中使用。

        1、同步方法

public synchronized 返回值 方法名(参数) {
    
}



public class SellTicket implements Runnable{

    private int ticket = 100;

    @Override
    public void run() {
        sellTicket();
    }

    public synchronized void sellTicket(){
            while (true){
                //synchronized(this){
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(ticket > 0){
                        System.out.println(Thread.currentThread().getName() + "卖完了" + ticket-- + "票");
                    }else{
                        System.out.println("票卖完了!");
                        break;
                    }
                //}
            }
    }

    public static void main(String[] args) {
        SellTicket sellTicket = new SellTicket();
        Thread thread = new Thread(sellTicket);
        Thread thread1 = new Thread(sellTicket);
        Thread thread2 = new Thread(sellTicket);
        thread.start();
        thread1.start();
        thread2.start();
    }
}

        2、同步代码块

public void show() {
    
    synchronized(锁对象) {
        
   }
    
}


public class SellTicket implements Runnable{

    private int ticket = 100;

    @Override
    public void run() {
        sellTicket();
    }

    public void sellTicket(){
            while (true){
                synchronized(this){
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(ticket > 0){
                        System.out.println(Thread.currentThread().getName() + "卖完了" + ticket-- + "票");
                    }else{
                        System.out.println("票卖完了!");
                        break;
                    }
                }
            }
    }

    public static void main(String[] args) {
        SellTicket sellTicket = new SellTicket();
        Thread thread = new Thread(sellTicket);
        Thread thread1 = new Thread(sellTicket);
        Thread thread2 = new Thread(sellTicket);
        thread.start();
        thread1.start();
        thread2.start();
    }
}

                通过锁对象可以将当前的代码段进行锁定,其他的线程需 要排队。作为同步方法一旦被整体修饰,就代表着整个方法内部都 不能够实现异步访问。 作为同步代码块,可以先调用synchronized修饰之前的代 码,提升程序的执行性能。

                3、静态同步代码块

public static synchronized 方法名() {
    
    
}
public static void show() {
    synchronized(当前类名的.class) {
        
   }  
    
}

                当在一个静态的方法上添加synchronized关键字,会采用 当前类的.class类信息作为锁对象。 如果是一个静态代码块,可以使用静态的变量进行修饰, 但是有可能会发生在外部修改锁对象的值。建议采用类的.class 作为锁对象。

                线程互斥:线程带有锁后,可能会出现互斥现象。静态的 同步方法或方法块,不会出现互斥的现象。

6、LOCK锁

        由于synchronized灵 活度较低,没有办法实现锁的监控过程(lock 、unlock)以及公平锁 的实现,所以在JDK5的版本中,在java.util.concurrent.locks包中 添加Lock接口,同时给了一些实现类。

package com.home;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable{

    private Lock lock = new ReentrantLock(true);
    private int ticket = 100;

    @Override
    public void run() {
        sellTicket();
    }

    public void sellTicket(){
            while (true){
                lock.lock();
                //synchronized(this){
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(ticket > 0){
                        System.out.println(Thread.currentThread().getName() + "卖完了" + ticket-- + "票");
                        lock.unlock();
                    }else{
                        System.out.println("票卖完了!");
                        lock.unlock();
                        break;
                    }
                //}
            }
    }

    public static void main(String[] args) {
        SellTicket sellTicket = new SellTicket();
        Thread thread = new Thread(sellTicket);
        Thread thread1 = new Thread(sellTicket);
        Thread thread2 = new Thread(sellTicket);
        thread.start();
        thread1.start();
        thread2.start();
    }
}

        1、synchronized与lock的区别

                synchronized是Java内置的一个线程同步关键字, 而Lock是J.U.C包下面的一个接口,它有很多实现类,比如 ReentrantLock就是它的一个实现类。 synchronized可以写在需要同步的对象、方法或者是特定代的 码块中。 Lock控制锁的粒度是通过lock() 和 unlock() 方法来实现的。 

                Lock比synchronized在使用上相对来说要更加灵活一些。Lock 可以自主地去决定什么时候加锁,什么时候释放锁。只需要调用 lock()和unlock()这两个方法就可以了。需要注意的是,为了避免死 锁,一般我们unlock()方法写在finally块中。 另外,Lock还提供了非阻塞的竞争锁的方法叫trylock(),这个 方法可以通过返回true或者fasle来告诉当前线程是否已经有其他线 程正在使用锁。 而synchronized是关键字,无法去扩展实现非阻塞竞争锁的方 法。另外,synchronized只有代码块执行结束或者代码出现异常的 时候才会释放锁,因此,它对锁的释放是被动的。 synchronized和Lock在性能上差别不大。在实现上有一些区 别, synchronized 采用的是悲观锁机制,synchronized 是托管给 JVM 执行的。在JDK1.6以后采用了偏向锁、轻量级锁、重量级锁及 锁升级的方式进行优化。 而 Lock 用的是乐观锁机制。控制锁的代码由用于自定义,也采 用CAS自旋锁进行了优化。 二者在一般情况下没有什么区别,但是在非常复杂的同步应用 中,建议使用Lock。 因为synchronized只提供了非公平锁的实现,而Lock提供了公 平所和非公平锁的机制。 公平锁是指线程竞争锁资源的时候,如果已经有其他线程正在 排队或者等待锁释放,那么当前竞争锁的线程是无法去插队的。 而非公平锁就是不管是否线程再排队等待锁,它都会去尝试竞 争一次锁。

        2、死锁

                1、什么是死锁

                                在多线程程序中,使用了多把锁,造成线程之间相互等待.程序不往下走了

                2、产生死锁的条件

                                1、有多把锁 2、有多个线程 3、有同步代码嵌套

                3、死锁实例

package Thread;

public class MyLock implements Runnable{
    Object obj1 = new Object();
    Object obj2 = new Object();

    @Override
    public void run() {
        synchronized (obj1) {
            System.out.println("嵌套1 obj1");
            synchronized (obj2) {// t2, obj1, 拿不到2锁,等待
                System.out.println("嵌套1 obj2");
            }
        }
        synchronized (obj2) {
            System.out.println("嵌套2 obj2");
            synchronized (obj1) {// t1 , obj2, 拿不到1锁,等待
                System.out.println("嵌套2 obj1");
            }
        }

    }
}


package Thread;

public class Test3 {
    public static void main(String[] args) {
        MyLock ml = new MyLock();
        new Thread(ml).start();
        new Thread(ml).start();
    }
}

7、并发包

        1、ConcurrentHashMap

public class Const {

    public static ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>() ;

//    public static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>() ;

    public static ArrayList<String> list = new ArrayList<>() ;
}



public class ConstHashMap extends Thread {


    @Override
    public void run() {
        Map<String, String> map = Collections.synchronizedMap(Const.map);
        for (int i = 0; i < 500000; i++) {
           map.put(this.getName() + (i+1) ,this.getName() + i + 1) ;
        }
        System.out.println("线程运行结束");
    }
}


public class TestHashMap {

    public static void main(String[] args) {

        ConstHashMap ch1 = new ConstHashMap() ;
        ConstHashMap ch2 = new ConstHashMap() ;

        ch1.start();

        ch2.start();

        try {
            Thread.sleep(5*1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("================" + Const.map.size());

    }

}

                        为什么要使用ConcurrentHashMap: 1. HashMap线程不安全,会导致数据错乱 2. 使用线程安全的Hashtable效率低下

        2、CountDownLatch

                CountDownLatch允许一个或多个线程等待其他线程完成操作,再执行自己。

public class PrintAC extends Thread{

    private CountDownLatch cdl ;

    public PrintAC(CountDownLatch cdl) {
        this.cdl = cdl ;
    }

    @Override
    public void run() {

        System.out.println("A");

        try {
            this.cdl.await();  //等待机制
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("D");

    }
}


public class PrintB extends Thread{

    private CountDownLatch cdl ;

    public PrintB(CountDownLatch cdl) {
        this.cdl = cdl ;
    }

    @Override
    public void run() {
        System.out.println("B");
        this.cdl.countDown();
        System.out.println("C");
        this.cdl.countDown();
    }
}


public class TestCountDownLatch {

    public static void main(String[] args) {

        CountDownLatch cdl = new CountDownLatch(2) ;

        PrintAC printAC = new PrintAC(cdl) ;

        PrintB printB = new PrintB(cdl) ;

        printAC.start();

        try {
            Thread.sleep(0);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        printB.start();

    }


}



        3、CyclicBarrier

                        CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一 个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线 程才会继续运行。

public class BossThread extends Thread{

    @Override
    public void run() {
        System.out.println("公司人太多了,需要开除几个员工");

    }
}


public class PersonThread extends Thread{

    private CyclicBarrier cyclicBarrier ;

    public PersonThread(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier ;
    }

    @Override
    public void run() {
        System.out.println("我们在等待老大的到来...");

        try {
            Thread.sleep(5000);

            System.out.println("老大要到了,我得提前准备准备");

            cyclicBarrier.await() ;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
        System.gc();

    }
}


public class TestMeeting {

    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new BossThread()) ;

        PersonThread p1 = new PersonThread(cyclicBarrier) ;
        PersonThread p2 = new PersonThread(cyclicBarrier) ;
        PersonThread p3 = new PersonThread(cyclicBarrier) ;
        PersonThread p4 = new PersonThread(cyclicBarrier) ;
        PersonThread p5 = new PersonThread(cyclicBarrier) ;

        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
    }
}

        4、Semaphore

                        Semaphore(发信号)的主要作用是控制线程的并发数量。 synchronized可以起到"锁"的作用,但某个时间段内,只能有一个线程允许执行。 Semaphore可以设置同时允许几个线程执行。 Semaphore字面意思是信号量的意思,它的作用是控制访问特定资源的线程数目。

public class ServiceImpl extends Thread{

    private Service service ;

    public ServiceImpl(Service service) {
        this.service = service ;
    }

    @Override
    public void run() {
        service.service();
    }
}


public class Service {

    //fair:是否以公平锁形式对线程放行
    private Semaphore semaphore = new Semaphore(30000,false) ;

    public void service() {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "该方法是一个对外服务的方法..");

        System.out.println("方法执行完成了...");

        semaphore.release();
    }

}


public class TestService {

    public static void main(String[] args) {
        Service service = new Service() ;

        for (int i = 0; i < 500000; i++) {
            new ServiceImpl(service).start();
        }

    }

}

        5、Exchanger

                   Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。 这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程 也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据 传递给对方。

public class ThreadA extends Thread {
    private Exchanger<String> exchanger;
    public ThreadA(Exchanger<String> exchanger) {
        super();
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        try {
            System.out.println("线程A欲传递值'礼物A'给线程B,并等待线程B的值...");
            System.out.println("在线程A中得到线程B的值=" + exchanger.exchange("礼物A"));
            } catch (InterruptedException e){
                e.printStackTrace();

            } 
    }
}


public class ThreadB extends Thread {
    private Exchanger<String> exchanger;
    public ThreadB(Exchanger<String> exchanger) {
        super();
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        try {
            System.out.println("线程B欲传递值'礼物B'给线程A,并等待线程A的值...");
            System.out.println("在线程B中得到线程A的值=" + exchanger.exchange("礼物B"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Exchanger<String> exchanger = new Exchanger<String>();
        ThreadA a = new ThreadA(exchanger);
        ThreadB b = new ThreadB(exchanger);
        a.start();
        b.start();
    }
}

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值