多线程总结(基础)

多线程与并发

多线程

线程的实现方法

继承Thread类

  1. 继承Thread类
  2. 重写run方法
  3. start开启线程
public class MyThread extends Thread{
    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName()+"-->haha");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("thread1");
        myThread.start();
    }
}

实现Runnable接口

  1. 实现Runnable接口
  2. 创建Thread对象
  3. 参数为实现类
  4. start启动线程
public class MyThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("hahaha");
    }

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

实现Callable接口

//1. 实现Callable,返回值类型为String
public class Thread3 implements Callable<String> {
    @Override
    // 2. 重写call方法
    public String call() throws Exception {
        System.out.println("hahahah");
        return "我是返回值";
    }
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //3. 打开服务,参数为服务个数
        ExecutorService service = Executors.newFixedThreadPool(1);
        //4. 提交执行
        Future<String> future = service.submit(new Thread3());
        //5. 获取返回值
        String s = future.get();
        //6. 结束服务
        service.shutdownNow();
    }
}

静态代理模式

案例:

public class StaticProxy {
  //************测试主方法*************
    public static void main(String[] args) {
        You you = new You();
        WeddingCompany weddingCompany = new WeddingCompany(you);
        weddingCompany.marry();
    }
}
 //*************************
//结婚接口
interface Marry {
  //结婚方法
    void marry();
}
 //*************************
//真实对象实现结婚接口
class You implements Marry {

  //重写结婚方法
    @Override
    public void marry() {
        System.out.println("要结婚了,哈哈哈");
    }
}
 //*************************
//代理对象实现结婚接口
class WeddingCompany implements Marry {
    private Marry target;
 //通过传入参数的方式调用真实对象
    public WeddingCompany(Marry target) {
        this.target = target;
    }
  //重写结婚方法
    @Override
    public void marry() {
        before();
        this.target.marry();
        after();
    }

    private void after() {
        System.out.println("收尾款");
    }

    private void before() {
        System.out.println("布置现场");
    }
}

核心点:
直接使用代理对象即可,为什么代理对象可以拥有真实对象的方法,因为new代理对象时,传参传了个真实对象!!

线程状态

停止线程

  • 不推荐使用JDK提供的stop() destory()废弃方法。
  • 可以使用flag=false让线程停止
public class Thread4 implements Runnable {
    private boolean flag = true;

    @Override
    public void run() {
        int i=0;
        while (flag) {
            System.out.println("thread4...."+i++);
        }
    }

    public void myStop() {
        this.flag = false;
    }

  //测试类
static class Test{
        public static void main(String[] args) {
            Thread4 thread4 = new Thread4();
            new Thread(thread4).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println(i);
                if (i==800){
                    thread4.myStop();
                }
            }
        }
    }
}

线程休眠sleep(抱着锁睡觉)

sleep不释放锁,其他线程必须等待它执行完毕才能执行

public class ThreadDemo {

  //lock对象将传入synchronized代码块里,为保证两个线程抢一把锁 
    private static Object lock = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (lock){
                try {
                    System.out.println("A休眠10秒不放弃锁");
                    Thread.sleep(10000);
                    System.out.println("A休眠10秒醒来");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();

        new Thread(()->{

            synchronized (lock){
                System.out.println("B休眠10秒不放弃锁");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("B休眠10秒醒来");
            }
        }).start();
    }
}
A休眠10秒不放弃锁
A休眠10秒醒来
B休眠10秒不放弃锁
B休眠10秒醒来
或者
B休眠10秒不放弃锁
B休眠10秒醒来
A休眠10秒不放弃锁
A休眠10秒醒来

线程礼让yield

public static void main(String[] args) {
        new Thread(() -> {
            System.out.println("A在执行。。。");
            Thread.yield();
            System.out.println("A结束了");
        }).start();
        
        new Thread(() -> {
            System.out.println("B在执行");
            Thread.yield();
            System.out.println("B结束了");
        }).start();
    }
A在执行
A结束了
B在执行
B结束了
  或者
A在执行
B在执行
A结束了
B结束了

线程插队join

**注意 **:谁调用让谁插队

public static void main(String[] args) {
        Thread threadA=new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("A在执行==》"+i);
                if (i==50){

                }
            }
        });
        threadA.start();

        Thread threadB=new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("B在执行==》"+i);
                if (i==50){
                    try {
                      // 让A线程插队
                        threadA.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        threadB.start();
    }

线程状态以及优先级

  thread.getState()
  thread.setPriority(int requestedPriority)
  thread.getPriority();

守护线程

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

 Thread daemonTread = new Thread();
  // 设定 daemonThread 为 守护线程,default false(非守护线程)
 daemonThread.setDaemon(true);
 // 验证当前线程是否为守护线程,返回 true 则为守护线程
 daemonThread.isDaemon();

并发

并发:同一个对象被多个线程同时操作

synchronized

修饰方法

public class synTest {
    public static void main(String[] args) {
        DemoService service = new DemoService();
        TestThread testThread = new TestThread(service);
        new Thread(testThread,"线程1:").start();
        new Thread(testThread,"线程2:").start();
    }
}

class DemoService {

     synchronized public void foo1() {
        System.out.println(Thread.currentThread().getName() + "foo1开始了。。。。");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "foo1结束了。。。。。");
    }

    synchronized public void foo2() {
        System.out.println(Thread.currentThread().getName() + "foo2开始了。。。。");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "foo2结束了。。。。。");
    }
}

class TestThread implements Runnable {
    private DemoService demoService;

    public TestThread(DemoService demoService) {
        this.demoService = demoService;
    }

    @Override
    public void run() {
        demoService.foo1();
        demoService.foo2();
    }
}

case1:foo1和foo2加锁时输出结果:

线程1:foo1开始了。。。。
线程1:foo1结束了。。。。。
线程1:foo2开始了。。。。
线程1:foo2结束了。。。。。
线程2:foo1开始了。。。。
线程2:foo1结束了。。。。。
线程2:foo2开始了。。。。
线程2:foo2结束了。。。。。

case2:foo1和foo2没加锁时输出结果:

线程1:foo1开始了。。。。
线程2:foo1开始了。。。。
线程2:foo1结束了。。。。。
线程2:foo2开始了。。。。
线程1:foo1结束了。。。。。
线程1:foo2开始了。。。。
线程1:foo2结束了。。。。。
线程2:foo2结束了。。。。。

case3:foo1加锁,foo2没解锁时:

线程1:foo1开始了。。。。
线程1:foo1结束了。。。。。
线程1:foo2开始了。。。。
线程2:foo1开始了。。。。
线程2:foo1结束了。。。。。
线程2:foo2开始了。。。。
线程1:foo2结束了。。。。。
线程2:foo2结束了。。。。。

总结:synchronized修饰方法时,锁的是调用此方法的对象,即为this。查看源码可以明白,修饰方法时候默认使用synchronized(this)。case1可以看出,无论线程1还是线程2,执行foo1还是foo2的时候由于对象被锁,所以调用此对象的其他线程无法进入。case2则特地乱套,两个线程随机执行对象方法。case3,由于foo1加锁,所以foo1之前期间不会被有其他线程进入。

同步代码块

public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            new Thread(() -> {
                synchronized (list) {
                    list.add(1);
                }
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }

Lock

class A{
            // 使用Lock的子类ReentrantLock
        private final ReentrantLock lock=new ReentrantLock();
        public void m(){
            lock.lock();
            try {
                // 代码块
            }finally {
                lock.unlock();
            }
        }
    }

synchronized和Lock的区别

<img src="../myhexo/source/images/image-20210428105514117.png" alt="image-20210428105514117" style="zoom:80%;" />

线程池

public class Testpool {
    public static void main(String[] args) {
        //1. 创建服务,创建线程池
        //newFixedThreadPool 参数为线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);
        // 执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2. 关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println("hahahaha");
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容